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 访问成功:

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ASP.NET Core实现自定义验证特性(Custom Validation Attribute)的步骤如下: 1. 创建一个继承自`ValidationAttribute`的自定义验证特性类,例如: ```csharp public class CustomValidationAttribute : ValidationAttribute { protected override ValidationResult IsValid(object value, ValidationContext validationContext) { // 验证逻辑 if (value is string str && str == "abc") { return ValidationResult.Success; } else { return new ValidationResult("必须是 abc"); } } } ``` 其中,`IsValid`方法是用来进行验证的,它接收两个参数:要验证的值和验证上下文。在该方法中,可以编写自定义的验证逻辑,并返回`ValidationResult`类型的结果。 2. 在需要验证的模型属性上添加自定义验证特性,例如: ```csharp public class MyModel { [CustomValidation] public string MyProperty { get; set; } } ``` 在这个例子中,`MyProperty`属性上添加了`CustomValidation`特性,表示在验证该属性时,会调用`CustomValidationAttribute`类中的`IsValid`方法。 3. 在控制器中进行验证,例如: ```csharp [HttpPost] public IActionResult MyAction([FromBody] MyModel model) { if (!ModelState.IsValid) { return BadRequest(ModelState); } // 其他逻辑 return Ok(); } ``` 在这个例子中,控制器的`MyAction`方法接收一个`MyModel`类型的参数,该参数会被自动绑定到请求体中。在方法中,可以通过`ModelState.IsValid`属性来判断模型是否验证通过,如果验证失败,则返回`BadRequest`结果,并将`ModelState`作为响应体返回。 以上就是在ASP.NET Core实现自定义验证特性的步骤。需要注意的是,自定义验证特性只是一种验证方式,如果需要更复杂的验证逻辑,可以使用`IValidatableObject`接口或自定义验证器来实现

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值