IdentityServer与JWT双重认证配置及ClaimsPrincipal问题咨询
问题描述
现有中心服务已使用IdentityServer作为认证方式,需保留该方式并新增JWT Bearer Token支持,有以下疑问:
- 是否可同时启用两种认证方式?
- 认证检查何时执行?是否都会运行?顺序是否重要?其中一种失败会怎样?
调试新Token时曾注释IdentityServer认证代码,当前中间件配置如下:
appBuilder.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions { "Authority" = ..., "ValidationMode" = ValidationMode.Both, "RequireHttps" = ..., "RequiredScopes" = ..., "AutomaticRefreshInterval" = ..., "ValidationResultCacheDuration" = ... }); appBuilder.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions { //"AuthenticationType" = DefaultAuthenticationTypes.ExternalBearer, // ??? "AuthenticationMode" = AuthenticationMode.Passive, // what is best??? "AllowedAudiences" = new[] { audience }, "TokenValidationParameters" = new TokenValidationParameters { "ValidateIssuerSigningKey" = true, "ValidateAudience" = false, "ValidateIssuer" = false, "IssuerSigningKey" = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(secret), //"ValidIssuer" = issuer //"ValidAudience" = audience, } });
当前遇到的障碍:我们通过AuthorizationFilterAttribute实现授权检查,代码如下:
public override Task OnAuthorizationAsync(HttpActionContext ac, CancellationToken cancellationToken) { if (!(ac.RequestContext.Principal is ClaimsPrincipal p) || !p.Identity.IsAuthenticated) { // 此处返回未授权,因为Principal不是ClaimsPrincipal类型 } return Task.FromResult(true); }
目前请求中仅能看到包含匿名WindowsIdentities的默认WindowsPrincipal,需要指导如何创建并添加ClaimsPrincipal。
解决方案
多认证方式相关疑问解答
是否可同时启用两种认证?
可以同时启用两种认证方式,ASP.NET支持配置多个认证中间件,只要配置逻辑正确就能共存。认证检查的执行逻辑
- 执行时机:认证中间件在请求管道的早期阶段执行,早于授权逻辑(
UseAuthorization),确保授权前完成身份验证。 - 是否都会运行:取决于认证模式(
AuthenticationMode):Active模式:中间件会主动尝试验证请求中的令牌;Passive模式:仅当通过[Authorize(AuthenticationSchemes = "指定认证类型")]明确指定时,才会触发验证。
- 顺序是否重要:非常重要。如果多个
Active模式中间件共存,会按注册顺序依次尝试验证,只要其中一个验证成功,后续Active中间件就会跳过;若所有Active中间件验证失败,才会触发指定使用的Passive中间件验证。 - 单种认证失败的影响:
Active模式中间件验证失败时,默认直接返回401未授权;Passive模式仅在被指定使用时,验证失败才会返回401,否则不影响其他认证流程。
中间件配置优化
针对你的JWT Bearer配置,建议调整如下:
- 设置
AuthenticationType:给JWT认证指定唯一标识(比如"JwtBearer"),方便后续在授权特性中指定使用,也能区分不同认证来源。 - 调整
AuthenticationMode:如果希望该中间件主动验证所有请求的JWT令牌,设为AuthenticationMode.Active;如果仅在特定接口使用,保持Passive并在对应接口的[Authorize]中指定AuthenticationSchemes = "JwtBearer"。
修改后的JWT配置示例:
appBuilder.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions { AuthenticationType = "JwtBearer", AuthenticationMode = AuthenticationMode.Active, // 根据业务需求选择模式 AllowedAudiences = new[] { audience }, TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, ValidateAudience = false, ValidateIssuer = false, IssuerSigningKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(secret), } });
解决ClaimsPrincipal缺失问题
当前仅能获取WindowsPrincipal,说明认证中间件未成功验证令牌并生成ClaimsPrincipal,可按以下步骤处理:
- 确认请求令牌格式:JWT令牌需放在
Authorization请求头中,格式为Bearer <token>;IdentityServer令牌也需符合对应格式要求。 - 检查中间件注册顺序:确保认证中间件注册在
UseWebApi/UseMvc之前,保证认证流程在API处理前完成。 - 调整授权过滤器:兼容多种Principal类型,或确保认证成功后生成ClaimsPrincipal,示例代码如下:
public override Task OnAuthorizationAsync(HttpActionContext ac, CancellationToken cancellationToken) { ClaimsPrincipal principal = null; // 尝试获取已认证的ClaimsPrincipal if (ac.RequestContext.Principal is ClaimsPrincipal cp) { principal = cp; } // 若为WindowsPrincipal,转换为ClaimsPrincipal(仅当启用Windows认证时适用) else if (ac.RequestContext.Principal is WindowsPrincipal wp) { principal = new ClaimsPrincipal(wp.Identity); } if (principal == null || !principal.Identity.IsAuthenticated) { ac.Response = ac.Request.CreateResponse(HttpStatusCode.Unauthorized); return Task.CompletedTask; } return Task.CompletedTask; }
- 开启认证日志:启用Microsoft IdentityModel的日志功能,查看认证过程中的错误信息,定位令牌验证失败的具体原因。
内容的提问来源于stack exchange,提问作者Ville
相关产品推荐
相关产品推荐

