IdentityServer4使用有效Access Token访问授权接口时返回401 Unauthorized问题排查
针对你遇到的问题,我会分场景逐步给出解决方案,覆盖从初始401错误到改用AddJwtBearer后的连接问题:
一、初始问题:获取Token后访问[Authorize]接口返回401
当你的应用同时作为IdentityServer4服务端和受保护API时,不能用AddIdentityServerAuthentication(这个方法是给独立API服务用的),正确的做法是用AddJwtBearer直接验证本地签发的JWT令牌。
1. 修正认证服务配置
替换原来的AddIdentityServerAuthentication代码,改为以下配置:
services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { // 指向你的IdentityServer运行地址 options.Authority = "http://localhost:41407"; // 开发环境关闭HTTPS元数据验证 options.RequireHttpsMetadata = false; options.TokenValidationParameters = new TokenValidationParameters { // 验证令牌受众,要和Config中ApiResource的名称一致 ValidateAudience = true, ValidAudience = "api1", // 验证签发者,要和appsettings中的Issuer一致 ValidateIssuer = true, ValidIssuer = "http://localhost:41407", // 确保令牌未过期 ValidateLifetime = true }; });
2. 修正中间件顺序
Configure方法中的中间件顺序直接影响认证逻辑,正确顺序如下(移除多余的UseAuthentication):
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "copyTrade v1")); } app.UseCors("CorsPolicy"); app.UseRouting(); // IdentityServer中间件要在授权之前,它已包含认证逻辑 app.UseIdentityServer(); // 只保留授权中间件即可 app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); // 本地化配置... }
3. 确保请求Token时包含正确的Scope
请求/connect/token时,必须在scope参数中包含api1,否则令牌不会被授予访问API的权限:
grant_type=password&username=你的用户名&password=你的密码&client_id=CopyTradeApi&client_secret=S23Mn67&scope=api1 openid profile email
二、改用AddJwtBearer后出现IDX20803连接错误
这个错误是因为JwtBearer中间件尝试访问IdentityServer的发现端点(/.well-known/openid-configuration)获取配置,但被代理拦截或本地端口不可达导致的。
解决方案1:禁用本地请求的代理
添加自定义HttpClient,强制禁用代理,避免请求被转发到1080端口:
services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.Authority = "http://localhost:41407"; options.RequireHttpsMetadata = false; options.TokenValidationParameters = new TokenValidationParameters { ValidateAudience = true, ValidAudience = "api1", ValidateIssuer = true, ValidIssuer = "http://localhost:41407", }; // 禁用代理,直接访问本地IdentityServer options.BackchannelHttpHandler = new HttpClientHandler { UseProxy = false, Proxy = null }; });
解决方案2:手动指定签名密钥(无需访问发现端点)
如果不想依赖发现端点,可以直接用IdentityServer的签名密钥验证令牌,跳过配置请求:
// 从配置中获取共享密钥(要和IdentityServer使用的密钥一致) var key = Encoding.UTF8.GetBytes(Configuration["BearerTokens:Key"]); services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.RequireHttpsMetadata = false; options.TokenValidationParameters = new TokenValidationParameters { ValidateAudience = true, ValidAudience = "api1", ValidateIssuer = true, ValidIssuer = "http://localhost:41407", ValidateIssuerSigningKey = true, // 手动指定签名密钥 IssuerSigningKey = new SymmetricSecurityKey(key) }; });
额外验证步骤
- 用jwt.io解析你的Token,确认:
exp字段未过期aud字段为api1iss字段为http://localhost:41407scope字段包含api1
- 手动访问
http://localhost:41407/.well-known/openid-configuration,确认能正常返回JSON配置,说明IdentityServer运行正常。
内容的提问来源于stack exchange,提问作者kamal gharejeloo
相关产品推荐
相关产品推荐

