ASP.NET Core 实现自定义认证

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ASP.NET Core 实现自定义认证相关的知识,希望对你有一定的参考价值。

前言

在 ASP.NET Core 中,我们常使用基于 JWT 的认证:

services.AddAuthentication(option =>

    option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

).AddJwtBearer(options =>

    options.TokenValidationParameters = new TokenValidationParameters
    
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = false,
        ValidateIssuerSigningKey = true,
        ValidIssuer = Configuration["JwtToken:Issuer"],
        ValidAudience = Configuration["JwtToken:Issuer"],
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtToken:SecretKey"]))
    ;
);

但有时候,我们需要使用自定义认证,比如使用QueryString(htttp://xxx?_key=xxx),只要请求中包含的_key的值正确即可。

AddJwtBearer 实现原理

为了实现自定义认证,我们决定仿照AddJwtBearer的实现机制。

AddJwtBearer实际执行的是AddScheme方法:

public static AuthenticationBuilder AddJwtBearer(this AuthenticationBuilder builder, string authenticationScheme, string? displayName, Action<JwtBearerOptions> configureOptions)

    builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<JwtBearerOptions>, JwtBearerPostConfigureOptions>());
    return builder.AddScheme<JwtBearerOptions, JwtBearerHandler>(authenticationScheme, displayName, configureOptions);

JwtBearerHandler是具体的处理程序,继承自AuthenticationHandler<TOptions>,主要代码在HandleAuthenticateAsync内:

protected override async Task<AuthenticateResult> HandleAuthenticateAsync()

  ...
 
  if (string.IsNullOrEmpty(token))
  
      string authorization = Request.Headers.Authorization.ToString();

      // 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();
      
  

  ...

  foreach (var validator in Options.SecurityTokenValidators)
  
      if (validator.CanReadToken(token))
      
        ...

        var tokenValidatedContext = new TokenValidatedContext(Context, Scheme, Options)
        
            Principal = principal,
            SecurityToken = validatedToken
        ;

        ...

        tokenValidatedContext.Success();
        return tokenValidatedContext.Result!;
      
  

  ...

Request.Headers.Authorization获取token,然后用Options.SecurityTokenValidators验证token合法后,返回结果。

Demo

DemoAuthenticationOptions

创建DemoAuthenticationOptions,继承自AuthenticationSchemeOptions:

public class DemoAuthenticationOptions : AuthenticationSchemeOptions

    public const string Scheme = "Demo";

DemoAuthenticationHandler

创建DemoAuthenticationHandler,继承自AuthenticationHandler<DemoAuthenticationOptions>:

public class DemoAuthenticationHandler : AuthenticationHandler<DemoAuthenticationOptions>

    public DemoAuthenticationHandler(IOptionsMonitor<DemoAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
    : base(options, logger, encoder, clock)
     
    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    
        throw new NotImplementedException();
    

实现 HandleAuthenticateAsync 方法

从请求的Query中获取key,然后检查是否合法:

protected async override Task<AuthenticateResult> HandleAuthenticateAsync()

    if (!Request.Query.TryGetValue("_key", out var keys))
    
        return AuthenticateResult.NoResult();
    

    var key = keys.FirstOrDefault();

    //到数据库检索
    if (key =="123456")
    
      var claims = new List<Claim>
      
          new Claim(ClaimTypes.Name, "My IO")
      ;

      var identity = new ClaimsIdentity(claims, DemoAuthenticationOptions.Scheme);
      var identities = new List<ClaimsIdentity>  identity ;
      var principal = new ClaimsPrincipal(identities);
      var ticket = new AuthenticationTicket(principal, DemoAuthenticationOptions.Scheme);

      return AuthenticateResult.Success(ticket);
    

    return AuthenticateResult.NoResult();

定义扩展方法

定义扩展方法,使用我们上面创建的DemoAuthenticationHandler

public static class AuthenticationBuilderExtensions

    public static AuthenticationBuilder AddDemoAuthentication(this AuthenticationBuilder authenticationBuilder, Action<DemoAuthenticationOptions> options)
    
        return authenticationBuilder.AddScheme<DemoAuthenticationOptions, DemoAuthenticationHandler>(DemoAuthenticationOptions.Scheme, options);
    

使用

修改Startup.cs:

services.AddAuthentication(option =>

    option.DefaultAuthenticateScheme = DemoAuthenticationOptions.Scheme;
    option.DefaultChallengeScheme = DemoAuthenticationOptions.Scheme;

)
    .AddDemoAuthentication(options =>  );

结论

当不加Query或使用错误的key时,返回401 认证失败:

仅当使用正确的key时,API 访问成功:

想了解更多内容,请关注我的个人公众号”My IO“

以上是关于ASP.NET Core 实现自定义认证的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core 搭载 Envoy 实现微服务身份认证(JWT)

ASP.NET Core 搭载 Envoy 实现微服务身份认证(JWT)

ASP.NET Core认证原理和实现

ASP.NET Core 实现基于 ApiKey 的认证

如何在 ASP.NET Core 中实现自定义模型验证?

ASP.NET Core 配置