当前位置 博文首页 > Asp.Net Core添加请求头自定义认证的示例

    Asp.Net Core添加请求头自定义认证的示例

    作者:MTiter 时间:2021-08-22 18:52

    目录
    • 前言
    • 要点
    • GuidToken 类就是我们自定义的 token 管理器
    • 最后就是使用方式

    前言

    小项目中需要添加 Api 请求权限认证, 并且只是专用网络内使用,于是只想简单得认证下是否可以访问, 顺便也是一种学习的过程,简单记录一下

    要点

    实现 IAuthenticationHandler 接口:4 个方法

    1. 首先会调用 InitializeAsync 获取到 scheme 和 context
    2. 然后调用 AuthenticateAsync ,在这里获取 context 中的 Header 中需要传过来的验证信息,然后进行相关验证,根据不同的结果会分别调用 ChallengeAsync 或者 ForbidAsync
    public class HeaderAuth : IAuthenticationHandler {
    
           public AuthenticationScheme Scheme { get; private set; }
    
           public HttpContext CurrentContext { get; private set; }
           public Task<AuthenticateResult> AuthenticateAsync() {
               var token = CurrentContext.Request.Headers[GuidToken.GUID_TOKEN_KEY].ToString();
    
               var (isValid, tokenEntity) = GuidToken.Valid(token);
    
               if (!isValid || tokenEntity == null) {
                   return Task.FromResult(AuthenticateResult.Fail("未登录或授权已过期。"));
               }
               // 生成 AuthenticationTicket
               AuthenticationTicket ticket = new AuthenticationTicket(tokenEntity.ToClaimsPrincipal(), Scheme.Name);
               return Task.FromResult(AuthenticateResult.Success(ticket));
           }
    
           public Task ChallengeAsync(AuthenticationProperties properties) {
               CurrentContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
               return Task.CompletedTask;
           }
    
           public Task ForbidAsync(AuthenticationProperties properties) {
               CurrentContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
               return Task.CompletedTask;
           }
    
           public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context) {
               Scheme = scheme;
               CurrentContext = context;
               return Task.CompletedTask;
           }
       }

    GuidToken 类就是我们自定义的 token 管理器

    public class GuidToken {
            public const string GUID_TOKEN_NAME = "MtGuidTokenAuthentication";
            public const string DEFAULT_AUTHENTICATION_TYPE = "local";
            public const int TOKEN_LENGTH = 32;
            public const string GUEST = "GUEST";
            public const string DEFAULT_ROLE = "USER";
            public const string DEFAULT_OPENID = "DEFAULT_OPENID";
            public const string GUID_TOKEN_KEY = "Token";
            private static int expireDuration = 0;
            public string OpenId { get; set; }
            public string Role { get; set; }
            public DateTime Expire { get; set; }
    
            private static readonly Dictionary<string, GuidToken> tokenCache = new Dictionary<string, GuidToken>();
    
            public static (bool, GuidToken) Valid(string token) {
                if (string.IsNullOrEmpty(token) || token.Length != TOKEN_LENGTH) {
                    return (false, null);
                }
    
                // 从 Session 中获取令牌实体
                GuidToken tokenEntity = GetTokenCache();
    
                if (tokenEntity == null) {
                    return (false, null);
                } else {
                    tokenEntity.Expire = DateTime.Now.AddMinutes(expireDuration);
                }
    
                return (true, tokenEntity);
    
                GuidToken GetTokenCache() {
                    if (tokenCache.TryGetValue(token, out var val)) {
                        if (val.Expire > DateTime.Now) return val;
                        else tokenCache.Remove(token);
                    }
                    return null;
                }
    
            }
    
            public static string Create(string openId = DEFAULT_OPENID, string role = DEFAULT_ROLE, int minutes = 30) {
                var token = Guid.NewGuid().ToString("N");
                expireDuration = minutes;
                var entity = new GuidToken {
                    OpenId = openId,
                    Role = role,
                    Expire = DateTime.Now.AddMinutes(expireDuration)
                };
                tokenCache.Add(token, entity);
                return token;
            }
    
            /// <summary>
            /// 令牌实体 转 ClaimsPrincipal
            /// </summary>
            /// <param name="token"></param>
            /// <returns></returns>
            public ClaimsPrincipal ToClaimsPrincipal() {
                var claimsIdentity = new ClaimsIdentity(new Claim[] {
                    new Claim(ClaimTypes.Name, OpenId),
                    new Claim(ClaimTypes.Role, Role),
                }, GuidToken.DEFAULT_AUTHENTICATION_TYPE);
    
                var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
    
                return claimsPrincipal;
            }
        }

    最后就是使用方式

    在 Startup 中配置

    public void ConfigureServices(IServiceCollection services) {
        // 注册使用
        services.AddAuthentication(options => {
            options.AddScheme<HeaderAuth>(GuidToken.GUID_TOKEN_NAME, "Default Guid Token");
            options.DefaultAuthenticateScheme = GuidToken.GUID_TOKEN_NAME;
            options.DefaultChallengeScheme = GuidToken.GUID_TOKEN_NAME;
        });
    }
    
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
        if (env.IsDevelopment()) {
            app.UseDeveloperExceptionPage();
        }
        app.UseCors("any");
        app.UseStaticFiles();
        // 开启认证
        app.UseAuthentication();
        app.UseRouting();
        // 开启授权
        app.UseAuthorization();
        app.UseEndpoints(endpoints => {
            endpoints.MapControllers();
        });
    }

    在控制器中使用标签

    [Authorize]
    public class JobController : ControllerBase {}
    jsjbwy