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

 

posted @ 2019-07-12 14:05  caolingyi  阅读(859)  评论(0编辑  收藏  举报