//堆代码 duidaima.com //多重身份认证 //默认使用JWT,如果Controller使用 AuthenticationSchemes 则采用指定的身份认证 Services.AddAuthentication(options => { options.AddScheme<CustomAuthenticationHandler>(CustomAuthenticationHandler.AuthenticationSchemeName, CustomAuthenticationHandler.AuthenticationSchemeName); options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.RequireHttpsMetadata = false;//设置元数据地址或权限是否需要HTTPs options.SaveToken = true; options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = builder.Configuration["Jwt:Issuer"], ValidAudience = builder.Configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:SecretKey"]!)) }; options.Events = new CustomJwtBearerEvents(); });自定义身份认证 CustomAuthenticationHandler.cs代码
public class CustomAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions> { public const string AuthenticationSchemeName = "CustomAuthenticationHandler"; private readonly IConfiguration _configuration; public CustomAuthenticationHandler( IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IConfiguration configuration) : base(options, logger, encoder, clock) { _configuration = configuration; } /// <summary> /// 固定Token认证 /// </summary> /// <returns></returns> protected override async Task<AuthenticateResult> HandleAuthenticateAsync() { string isAnonymous = Request.Headers["IsAnonymous"].ToString(); if (!string.IsNullOrEmpty(isAnonymous)) { bool isAuthenticated = Convert.ToBoolean(isAnonymous); if (isAuthenticated) return AuthenticateResult.NoResult(); } string authorization = Request.Headers["Authorization"].ToString(); // "Bearer " --> Bearer后面跟一个空格 string token = authorization.StartsWith("Bearer ") ? authorization.Remove(0, "Bearer ".Length) : authorization; if (string.IsNullOrEmpty(token)) return AuthenticateResult.Fail("请求头Authorization不允许为空。"); //通过密钥,进行加密、解密对比认证 if (!VerifyAuthorization(token)) return AuthenticateResult.Fail("传入的Authorization身份验证失败。"); return AuthenticateResult.Success(GetTicket()); } private AuthenticationTicket GetTicket() { // 验证成功,创建身份验证票据 var claims = new[] { new Claim(ClaimTypes.Role, "Admin"), new Claim(ClaimTypes.Role, "Public"), }; var identity = new ClaimsIdentity(claims, Scheme.Name); var principal = new ClaimsPrincipal(identity); var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), this.Scheme.Name); return ticket; } private bool VerifyAuthorization(string token) { //token: [0]随机生成64位字符串,[1]载荷数据,[2]采用Hash对[0]+[1]的签名 var tokenArr = token.Split('.'); if (tokenArr.Length != 3) { return false; } try { //1、先比对签名串是否一致 string signature = tokenArr[1].Hmacsha256HashEncrypt().ToLower(); if (!signature.Equals(tokenArr[2].ToLower())) { return false; } //解密 var aecStr = tokenArr[1].Base64ToString(); var clientId = aecStr.DecryptAES(); //2、再验证载荷数据的有效性 var clientList = _configuration.GetSection("FixedClient").Get<List<FixedClientSet>>(); var clientData = clientList.SingleOrDefault(it => it.ClientID.Equals(clientId)); if (clientData == null) { return false; } } catch (Exception) { throw; } return true; } }使用中间件:UseMiddleware
app.UseAuthentication(); //中间件模式:自定义认证中间件:双重认证选其一 //如果使用 策略,需要注释掉 中间件 app.UseMiddleware<FallbackAuthenticationMiddleware>(); //使用中间件实现 app.UseAuthorization(); 中间件FallbackAuthenticationMiddleware.cs代码实现 public class FallbackAuthenticationMiddleware { private readonly RequestDelegate _next; private readonly IAuthenticationSchemeProvider _schemeProvider; public FallbackAuthenticationMiddleware(RequestDelegate next, IAuthenticationSchemeProvider schemeProvider) { _next = next; _schemeProvider = schemeProvider; } /// <summary> /// 身份认证方案 /// 默认JWT。JWT失败,执行自定义认证 /// </summary> /// <param name="context"></param> /// <returns></returns> public async Task InvokeAsync(HttpContext context) { var endpoints = context.GetEndpoint(); if (endpoints == null || !endpoints.Metadata.OfType<IAuthorizeData>().Any() || endpoints.Metadata.OfType<IAllowAnonymous>().Any()) { await _next(context); return; } //默认JWT。JWT失败,执行自定义认证 var result = await Authenticate_JwtAsync(context); if (!result.Succeeded) result = await Authenticate_CustomTokenAsync(context); // 设置认证票据到HttpContext中 if (result.Succeeded) context.User = result.Principal; await _next(context); } /// <summary> /// JWT的认证 /// </summary> /// <param name="context"></param> /// <returns></returns> private async Task<dynamic> Authenticate_JwtAsync(HttpContext context) { var verify = context.User?.Identity?.IsAuthenticated ?? false; string authenticationType = context.User.Identity.AuthenticationType; if (verify && authenticationType != null) { return new { Succeeded = verify, Principal = context.User, Message = "" }; } await Task.CompletedTask; // 找不到JWT身份验证方案,或者无法获取处理程序。 return new { Succeeded = false, Principal = new ClaimsPrincipal { }, Message = "JWT authentication scheme not found or handler could not be obtained." }; } /// <summary> /// 自定义认证 /// </summary> /// <param name="context"></param> /// <returns></returns> private async Task<dynamic> Authenticate_CustomTokenAsync(HttpContext context) { // 自定义认证方案的名称 var customScheme = "CustomAuthenticationHandler"; var fixedTokenHandler = await context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>().GetHandlerAsync(context, customScheme); if (fixedTokenHandler != null) { var Res = await fixedTokenHandler.AuthenticateAsync(); return new { Res.Succeeded, Res.Principal, Res.Failure?.Message }; } //找不到CustomAuthenticationHandler身份验证方案,或者无法获取处理程序。 return new { Succeeded = false, Principal = new ClaimsPrincipal { }, Message = "CustomAuthenticationHandler authentication scheme not found or handler could not be obtained." }; } }方案二:通过[Authorize]标签的AuthenticationSchemes
//使用特定身份认证 //[Authorize(AuthenticationSchemes = CustomAuthenticationHandler.AuthenticationSchemeName)] //任一身份认证 [Authorize(AuthenticationSchemes = $"{CustomAuthenticationHandler.AuthenticationSchemeName},{JwtBearerDefaults.AuthenticationScheme}")] public class DataProcessingController : ControllerBase { }方案三:通过[Authorize]标签的policy
//授权策略 //Controller使用 policy 则采用指定的策略配置进行身份认证 builder.Services.AddAuthorization(option => { option.AddPolicy(CustomPolicy.Policy_A, policy => policy .RequireAuthenticatedUser() .AddAuthenticationSchemes(CustomAuthenticationHandler.AuthenticationSchemeName, JwtBearerDefaults.AuthenticationScheme) ); option.AddPolicy(CustomPolicy.Policy_B, policy => policy .RequireAuthenticatedUser() .AddAuthenticationSchemes(CustomAuthenticationHandler.AuthenticationSchemeName) ); option.AddPolicy(CustomPolicy.Policy_C, policy => policy .RequireAuthenticatedUser() .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme) ); }); //使用特定策略身份认证 [Authorize(policy:CustomPolicy.Policy_B)] public class DataProcessingController : ControllerBase { } /// <summary> /// 策略类 /// </summary> public static class CustomPolicy { public const string Policy_A= "Policy_A"; public const string Policy_B = "Policy_B"; public const string Policy_C = "Policy_C"; }最后附上截图:
源码Demo:https://gitee.com/LaoPaoE/project-demo.git
ASP.NET Core会根据这些认证方案对用户进行身份验证。如果用户未通过身份验证(即未登录或未提供有效的认证信息),则请求会被拒绝,并可能重定向到登录页面。
ASP.NET Core会检查用户的角色信息,以确定用户是否属于 Roles 属性中指定的一个或多个角色。
如果用户不满足策略中的任何要求,则授权失败,并返回一个HTTP 403 Forbidden响应。
用户必须同时满足AuthenticationSchemes、Roles(如果指定)和Policy(如果指定)中的所有条件,才能成功访问受保护的资源。