• ASP.NET Core 学习笔记(认证授权)


    https://docs.microsoft.com/zh-cn/aspnet/core/migration/1x-to-2x/identity-2x?view=aspnetcore-2.2

    1、Cookie-based authentication

    2、JWT Bearer Authentication

    3、OpenID Connect (OIDC) authentication

    JWT (https://jwt.io/):

    应用场景:移动端,前后端分离项目,API 请求

    使用示例:

    services.AddAuthentication(options =>
                {
                    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                }).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, jwtBearerOptions =>
                    {
                        jwtBearerOptions.RequireHttpsMetadata = false;
                        jwtBearerOptions.SaveToken = false;
                        jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters
                        {
                            ValidateIssuer = true,//是否验证Issuer
                            ValidateAudience = true,//是否验证Audience
                            ValidIssuer = Configuration["Tokens:ValidIssuer"],
                            ValidAudience = Configuration["Tokens:ValidAudience"],
                            ValidateIssuerSigningKey = true,
                            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Tokens:Key"])),
                            ValidateLifetime = true, //validate the expiration and not before values in the token
                            ClockSkew = TimeSpan.FromMinutes(5) //5 minute tolerance for the expiration date
                        };
                    });

     Discovery Endpoint(/.well-known/openid-configuration)

    {
        "issuer": "http://192.168.1.7:50081",
        "jwks_uri": "http://192.168.1.7:50081/.well-known/openid-configuration/jwks",
        "authorization_endpoint": "http://192.168.1.7:50081/connect/authorize",
        "token_endpoint": "http://192.168.1.7:50081/connect/token",
        "userinfo_endpoint": "http://192.168.1.7:50081/connect/userinfo",
        "end_session_endpoint": "http://192.168.1.7:50081/connect/endsession",
        "check_session_iframe": "http://192.168.1.7:50081/connect/checksession",
        "revocation_endpoint": "http://192.168.1.7:50081/connect/revocation",
        "introspection_endpoint": "http://192.168.1.7:50081/connect/introspect",
        "device_authorization_endpoint": "http://192.168.1.7:50081/connect/deviceauthorization",
        "frontchannel_logout_supported": true,
        "frontchannel_logout_session_supported": true,
        "backchannel_logout_supported": true,
        "backchannel_logout_session_supported": true,
        "scopes_supported": ["openid", "profile", "Asmkt.UnifyAdmin.Api", "Asmkt.UnifyAdmin.SignalrHub", "offline_access"],
        "claims_supported": ["sub", "name", "family_name", "given_name", "middle_name", "nickname", "preferred_username", "profile", "picture", "website", "gender", "birthdate", "zoneinfo", "locale", "updated_at"],
        "grant_types_supported": ["authorization_code", "client_credentials", "refresh_token", "implicit", "urn:ietf:params:oauth:grant-type:device_code"],
        "response_types_supported": ["code", "token", "id_token", "id_token token", "code id_token", "code token", "code id_token token"],
        "response_modes_supported": ["form_post", "query", "fragment"],
        "token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post"],
        "subject_types_supported": ["public"],
        "id_token_signing_alg_values_supported": ["RS256"],
        "code_challenge_methods_supported": ["plain", "S256"]
    }

    一个携带 Token 的请求从认证中间件到最终验证 Issuer 的逻辑参考:晓晨博客 https://www.cnblogs.com/stulzq/p/10339024.html

     涉及类型方法如下:

    颁发token :

    var claims = new List<Claim>
                {
                    new Claim(ClaimTypes.Name, user.UserName),
                };
                var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenKey));
                var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
                var token = new JwtSecurityToken(
                    issuer: issuer,
                    audience: audience,
                    claims: claims,
                    notBefore: DateTime.Now,
                    expires: expires,
                    signingCredentials: creds
                );
                return new JwtSecurityTokenHandler().WriteToken(token);

     JWT定制:(自定义token来源及验证)

    JwtBearerHandler:

     // Give application opportunity to find from a different location, adjust, or reject token
                    var messageReceivedContext = new MessageReceivedContext(Context, Scheme, Options);
    
                    // event can set the token
                    await Events.MessageReceived(messageReceivedContext);
                    if (messageReceivedContext.Result != null)
                    {
                        return messageReceivedContext.Result;
                    }
    
                    // If application retrieved token from somewhere else, use that.
                    token = messageReceivedContext.Token;
    
                    if (string.IsNullOrEmpty(token))
                    {
                        string authorization = Request.Headers["Authorization"];
    
                        // If no authorization header found, nothing to process further
                        if (string.IsNullOrEmpty(authorization))
                        {
                            return AuthenticateResult.NoResult();
                        }
    
                        if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
                        {
                            token = authorization.Substring("Bearer ".Length).Trim();
                        }
    
                        // If no token found, no further work possible
                        if (string.IsNullOrEmpty(token))
                        {
                            return AuthenticateResult.NoResult();
                        }
                    }

    由上源码可知 Token 可从messageReceivedContext 获取,当没有值时才从header 中根据 key Authorization 来获取token.

    JwtBearerEvents 提供了定制来源,定制验证的接口

    /// <summary>
        /// 自定义token验证
        /// </summary>
        public class MyTokenValidator : ISecurityTokenValidator
        {
            public bool CanValidateToken => true;
    
            public int MaximumTokenSizeInBytes { get; set; }
    
            public bool CanReadToken(string securityToken)
            {
                return true;
            }
    
            public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
            {
                validatedToken = null;
                if (securityToken != "123")
                {
                    return null;
                }
                var identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme);
                identity.AddClaim(new Claim(ClaimTypes.Name, "caolingyi"));
    
                var principal = new ClaimsPrincipal(identity);
                return principal;
    
            }
        }

    使用:

     .AddJwtBearer(option =>
                {
                    option.SecurityTokenValidators.Clear();//原先默认的验证方法清除
                    option.SecurityTokenValidators.Add(new MyTokenValidator());
                    option.Events = new JwtBearerEvents
                    {
                        OnMessageReceived = context =>
                        {
                            var token = context.Request.Headers["mytoken"];
                            context.Token = token.FirstOrDefault();
                            return Task.CompletedTask;
                        }
                    };
                });

    基于角色的授权、声明的授权、策略的授权

    https://docs.microsoft.com/zh-cn/aspnet/core/security/authorization/simple?view=aspnetcore-2.2

     services.AddAuthorization(options =>
        {
            options.AddPolicy("RequireAdministratorRole",
                 policy => policy.RequireRole("Administrator"));
        });
    
    [Authorize(Policy = "RequireAdministratorRole")]
    
    
    services.AddAuthorization(options =>
        {
            options.AddPolicy("EmployeeOnly", policy => policy.RequireClaim("EmployeeNumber"));
        });
    
    [Authorize(Policy = "EmployeeOnly")]
    
    services.AddAuthorization(options =>
        {
            options.AddPolicy("Founders", policy =>
                              policy.RequireClaim("EmployeeNumber", "1", "2", "3", "4", "5"));
        });
    
    
    services.AddAuthorization(options =>
        {
            options.AddPolicy("AtLeast21", policy =>
                policy.Requirements.Add(new MinimumAgeRequirement(21)));
        });
    
    
    using Microsoft.AspNetCore.Authorization;
    
    public class MinimumAgeRequirement : IAuthorizationRequirement
    {
        public int MinimumAge { get; }
    
        public MinimumAgeRequirement(int minimumAge)
        {
            MinimumAge = minimumAge;
        }
    }
    using System;
    using System.Security.Claims;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Authorization;
    using PoliciesAuthApp1.Services.Requirements;
    
    public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
    {
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                       MinimumAgeRequirement requirement)
        {
            if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth &&
                                            c.Issuer == "http://contoso.com"))
            {
                //TODO: Use the following if targeting a version of
                //.NET Framework older than 4.6:
                //      return Task.FromResult(0);
                return Task.CompletedTask;
            }
    
            var dateOfBirth = Convert.ToDateTime(
                context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth && 
                                            c.Issuer == "http://contoso.com").Value);
    
            int calculatedAge = DateTime.Today.Year - dateOfBirth.Year;
            if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge))
            {
                calculatedAge--;
            }
    
            if (calculatedAge >= requirement.MinimumAge)
            {
                context.Succeed(requirement);
            }
    
            //TODO: Use the following if targeting a version of
            //.NET Framework older than 4.6:
            //      return Task.FromResult(0);
            return Task.CompletedTask;
        }
    }

     [Authorize(Policy = "AtLeast21")] 

    多策略参考官方文档

     
    i
    dentityserver4 参考文档:

    官方文档 :https://identityserver4.readthedocs.io/en/latest/

    源代码:https://github.com/IdentityServer

    OAuth2.0:http://www.ruanyifeng.com/blog/2019/04/oauth-grant-types.html

  • 相关阅读:
    Zigbee安全基础篇Part.3
    Zigbee安全基础篇Part.2
    Zigbee安全基础篇Part.1
    mini2440 Nor Flash工作原理分析
    fuck the browser mode
    valgrind使用
    windows下自己常用的几个bat
    二叉树可视化
    npm的使用
    tp5的phpword使用
  • 原文地址:https://www.cnblogs.com/caolingyi/p/11171702.html
Copyright © 2020-2023  润新知