.Net Core中自定义认证实现

2022-04-16 19:15:42
目录
一、起因二、自定义认证实现 三、多认证支持四、总结

一、起因

 最近项目中需要对项目同时支持JWT认证,以及自定义的认证校验方式认证。通过对官方文档了解,得到认证实现主要通过继承 IAuthenticationHandler 或 AuthenticationHandler<TOptions>来实现自定义认证的处理。 

 那么接下来实现一个自定义的认证访问。

二、自定义认证实现 

 1、根据前面内容得知,处理认证通过IAuthenticationHandler 实例处理;那么首先添加一个自定义IAuthenticationHandler 类型:

/// <summary>/// 方式一:自定义认证处理器/// </summary>public class CustomerAuthenticationHandler : IAuthenticationHandler{  private IUserService _userService;  public CustomerAuthenticationHandler(IUserService userService)  {    _userService = userService;  }  /// <summary>  /// 自定义认证Scheme名称  /// </summary>  public const string CustomerSchemeName = "cusAuth";  private AuthenticationScheme _scheme;  private HttpContext _context;  /// <summary>  /// 认证逻辑:认证校验主要逻辑  /// </summary>  /// <returns></returns>  public Task<AuthenticateResult> AuthenticateAsync()  {    AuthenticateResult result;    _context.Request.Headers.TryGetValue("Authorization", out StringValues values);    string valStr = values.ToString();    if (!string.IsNullOrWhiteSpace(valStr))    {      //认证模拟basic认证:cusAuth YWRtaW46YWRtaW4=      string[] authVal = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(valStr.Substring(CustomerSchemeName.Length + 1))).Split(':');      var loginInfo = new Dto.LoginDto() { Username = authVal[0], Password = authVal[1] };      var validVale = _userService.IsValid(loginInfo);      if (!validVale)        result = AuthenticateResult.Fail("未登陆");      else      {        var ticket = GetAuthTicket(loginInfo.Username, "admin");        result = AuthenticateResult.Success(ticket);      }    }    else    {      result = AuthenticateResult.Fail("未登陆");    }    return Task.FromResult(result);  }  /// <summary>  /// 未登录时的处理  /// </summary>  /// <param name="properties"></param>  /// <returns></returns>  public Task ChallengeAsync(AuthenticationProperties properties)  {    _context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;    return Task.CompletedTask;  }  /// <summary>  /// 权限不足时处理  /// </summary>  /// <param name="properties"></param>  /// <returns></returns>  public Task ForbidAsync(AuthenticationProperties properties)  {    _context.Response.StatusCode = (int)HttpStatusCode.Forbidden;    return Task.CompletedTask;  }  /// <summary>  /// 初始化认证  /// </summary>  /// <param name="scheme"></param>  /// <param name="context"></param>  /// <returns></returns>  public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)  {    _scheme = scheme;    _context = context;    return Task.y(new Claim[]    {  new Claim(ClaimTypes.Name, name),  new Claim(ClaimTypes.Role, role),    }, CustomerSchemeName);    var principal = new ClaimsPrincipal(claimsIdentity);    return new AuthenticationTicket(principal, _scheme.Name);  }  #endregion}/// <summary>/// 方式二:继承已实现的基类/// </summary>public class SubAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>{  public SubAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)    : base(options, logger, encoder, clock)  {  }  protected ovIYWFEoerride Task<AuthenticateResult> HandleAuthenticateAsync()  {    throw new NotImplementedException();  }}

 2、在Startup.cs中启用自定义认证:

public void ConfigureServices(IServiceCollection services){  //other code  services.AddAuthentication(o =>  {    x.DefaultAuthenticateScheme = CustomerAuthenticationHandler.CustomerSchemeName;    x.DefaultChallengeScheme = CustomerAuthenticationHandler.CustomerSchemeName;    o.AddScheme<CustomerAuthenticationHandler>(CustomerAuthenticationHandler.CustomerSchemeName, CustomerAuthenticationHandler.CustomerSchemeName);  });  //other code}public void Configure(IApplicationBuilder app){  //other code  app.UseRouting();  //在UseRouting后;UseEndpoints前添加以下代码  app.UseAuthentication();IYWFEo  app.UseAuthorization();  //other code  app.UseEndpoints()}

 3、在控制器上添加认证标记,测试验证

//指定认证时,采用CustomerAuthenticationHandler.CustomerSchemeName[Authorize(AuthenticationSchemes = CustomerAuthenticationHandler.CustomerSchemeName)][Route("api/[controller]")][ApiController]public class AuditLogController : ControllerBase{//code}

  调用

.Net Core中自定义认证实现

三、多认证支持

 在实际项目中可能存在,对一个控制器支持多种认证方式如:常用的Jwt认证、自定义认证等,那么如何实现呢?

 1、在Startup的ConfigureServices 方法中添加以下逻辑:

public void ConfigureServices(IServiceCollection services){  //other code  services.Configure<JwtSetting>(Configuration.GetSection("JWTSetting"));  var token = Configuration.GetSection("JWTSetting").Get<JwtSetting>();  //JWT认证  services.AddAuthentication(x =>  {    x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;    x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;     //添加自定义认证处理器    x.AddScheme<CustomerAuthenticationHandler>(CustomerAuthenticationHandler.CustomerSchemeName, CustomerAuthenticationHandler.CustomerSchemeName);  }).AddJwtBearer(x =>  {    x.RequireHttpsMetadata = false;    x.SaveToken = true;    x.TokenValidationParameters = new TokenValidationParameters    {      ValidateIssuerSigningKey = true,      IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(token.SecretKey)),      ValidIssuer = token.Issuer,      ValidAudience = token.Audience,      ValidateIssuer = false,      ValidateAudience = false    };  });  //other code}

 2、在需要支持多种认证方式的控制器上添加标记:

//指定认证时,采用CustomerAuthenticationHandler.CustomerSchemeName[Authorize(AuthenticationSchemes = CustomerAuthenticationHandler.CustomerSchemeName)][Route("api/[controller]")][ApiController]public class AuditLogController : ControllerBase{//code}//指定认证采用JWT[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]public class WeatherForecastController : ControllerBase{ //code}

   这样就支持了两种认证方式

 3、一个控制器支持多种认证类型:继承Jwt认证处理,并根据Scheme那么调用自定义的认证处理器:

/// <summary>/// 方式二:同时支持多种认证方式/// </summary>public class MultAuthenticationHandler : JwtBearerHandler{  public const string MultAuthName = "MultAuth";  IUserService _userService;  public MultAuthenticationHandler(IOptionsMonitor<JwtBearerOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IUserService userService)    : base(options, logger, encoder, clock)  {    _userService = userService;  }  protected override Task<AuthenticateResult> HandleAuthenticateAsync()  {    Context.Request.Headers.TryGetValue("Authorization", out StringValues values);    string valStr = values.ToString();    if (valStr.StartsWith(CustomerAuthenticationHandler.CustomerSchemeName))    {      var result = Valid();      if (result != null)        return Task.FromResult(AuthenticateResult.Success(result));      else        return Task.FromResult(AuthenticateResult.Fail("未认证"));    }    else      return base.AuthenticateAsync();  }  private AuthenticationTicket Valid()  {    Context.Request.Headers.TryGetValue("Authorization", out StringValues values);    string valStr = values.ToString();    if (!string.IsNullOrWhiteSpace(valStr))    {      //认证模拟basic认证:cusAuth YWRtaW46YWRtaW4=      string[] authVal = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(valStr.Substring(CustomerAuthenticationHandler.CustomerSchemeName.Length + 1))).Split(':');      var loginInfo = new Dto.LoginDto() { Username = authVal[0], Password = authVal[1] };      if (_userService.IsValid(loginInfo))        return GetAuthTicket(loginInfo.Username, "admin");    }    return null;  }  /// <summary>  /// 生成认证票据  /// </summary>  /// <param name="name"></param>  /// <pwww.easck.comaram name="role"></param>  /// <returns></returns>  private AuthenticationTicket GetAuthTicket(string name, string role)  {    var claimsIdentity = new ClaimsIdentity(new Claim[]    {      new Claim(ClaimTypes.Name, name),      new Claim(ClaimTypes.Role, role),    }, CustomerAuthenticationHandler.CustomerSchemeName);    var principal = new ClaimsPrincipal(claimsIdentity);    return new AuthenticationTicket(principal, CustomerAuthenticationHandler.CustomerSchemeName);  }}

四、总结

  .Net Core中的自定义认证主要通过实现IAuthenticationHandler 接口实现,如果要实现多认证方式通过AddScheme 应用自定义实现的认证处理器。

 源码:github