IdentityServer4服务间调用返回401授权错误求助
排查IdentityServer4客户端调用后端接口401未授权问题
我正在学习ASP.NET和IdentityServer4,按教程搭建了令牌生成服务器。目前Postman和客户端控制器都能成功获取令牌,但客户端调用需要授权的后端接口时出现401未授权错误,Postman调用该接口却能正常通过。
IdentityServer配置类
public static class Configuration { public static IEnumerable<ApiScope> ApiScopes => new List<ApiScope> { new ApiScope("NotesWebAPI", "Web API") }; public static IEnumerable<IdentityResource> IdentityResources => new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile(), }; public static IEnumerable<ApiResource> ApiResources => new List<ApiResource> { new ApiResource("NotesWebAPI", "Web API", new []{ JwtClaimTypes.Name }) { Scopes = { "NotesWebAPI" } } }; public static IEnumerable<Client> Clients => new List<Client> { new Client { ClientId = "client_id", ClientSecrets = { new Secret("client_secret".ToSha256()) }, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = { "NotesWebAPI", IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile } }, }; }
IdentityServer的Startup类
public class Startup { IConfiguration AppConfiguration { get; } public Startup(IConfiguration config) { AppConfiguration = config; } public void ConfigureServices(IServiceCollection services) { services.AddIdentity<AppUser, IdentityRole>(config => { config.Password.RequiredLength = 6; config.Password.RequireDigit = false; config.Password.RequireNonAlphanumeric = false; config.Password.RequireUppercase = false; config.Password.RequireLowercase = false; }) .AddEntityFrameworkStores<AuthDbContext>() .AddDefaultTokenProviders(); services.AddDbContext<AuthDbContext>(options => options.UseSqlServer( AppConfiguration.GetConnectionString("DefaultConnection"))); services.AddIdentityServer() .AddAspNetIdentity<AppUser>() .AddInMemoryApiResources(Configuration.ApiResources) .AddInMemoryIdentityResources(Configuration.IdentityResources) .AddInMemoryApiScopes(Configuration.ApiScopes) .AddInMemoryClients(Configuration.Clients) .AddDeveloperSigningCredential(); services.ConfigureApplicationCookie(config => { config.Cookie.Name = "Notes.Identity.Cookie"; //config.LoginPath = "/Auth/Login"; //config.LogoutPath = "/Auth/Logout"; }); services.AddControllersWithViews(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseStaticFiles(new StaticFileOptions { FileProvider = new PhysicalFileProvider( Path.Combine(env.ContentRootPath, "Styles")), RequestPath = "/styles" }); app.UseRouting(); app.UseIdentityServer(); app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute(); }); } }
后端服务的Startup类
private IConfiguration Configuration { get; } public Startup(IConfiguration config) { Configuration = config; } public void ConfigureServices(IServiceCollection services) { services.AddAutoMapper(config => { config.AddProfile(new AssemblyMappingProfile(Assembly.GetExecutingAssembly())); config.AddProfile(new AssemblyMappingProfile(typeof(INotesDbContext).Assembly)); }); services.AddApplication(); services.AddPersistence(Configuration); services.AddControllers(); services.AddCors(config => { config.AddPolicy("DefaultPolicy", builder => builder .AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader()); }); services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, config => { config.TokenValidationParameters = new TokenValidationParameters { ClockSkew = TimeSpan.FromMinutes(1), ValidateAudience = false }; config.Authority = "https://localhost:5001"; config.Audience = "NotesWebAPI"; //config.Audience = "https://localhost:5001"; }); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseCustomExceptionHandler(); app.UseRouting(); app.UseHttpsRedirection(); app.UseCors("DefaultPolicy"); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
客户端控制器代码
[Route("[action]")] public async Task<IActionResult> GetNotes() { var authClient = _httpClientFactory.CreateClient(); var discoverDocument = await authClient.GetDiscoveryDocumentAsync("http://localhost:5000"); var tokenResponse = await authClient.RequestClientCredentialsTokenAsync( new ClientCredentialsTokenRequest { Address = discoverDocument.TokenEndpoint, ClientId = "client_id", ClientSecret = "client_secret", Scope = "NotesWebAPI", }); var requestClient = _httpClientFactory.CreateClient(); requestClient.SetBearerToken(tokenResponse.AccessToken); var responce = await requestClient.GetAsync("http://localhost:1321/api/note"); if (!responce.IsSuccessStatusCode) { ViewBag.Message = responce.StatusCode.ToString(); return View(); } var message = await responce.Content.ReadAsStringAsync(); ViewBag.Message = message; return View(); }
排查方向及解决方案
1. Authority地址不匹配
客户端获取令牌用的是http://localhost:5000,但后端服务配置的Authority是https://localhost:5001。IdentityServer生成的令牌中iss字段是请求地址的域名,后端验证时会校验该字段是否与配置的Authority一致,不一致直接返回401。
解决:
统一客户端和后端的Authority地址,比如都用HTTPS地址:
// 客户端代码修改 var discoverDocument = await authClient.GetDiscoveryDocumentAsync("https://localhost:5001");
2. Audience验证配置冲突
后端同时设置了ValidateAudience = false和config.Audience = "NotesWebAPI",这会导致配置逻辑冲突。即使关闭了Audience验证,显式指定Audience仍可能触发验证逻辑。
解决:
要么开启Audience验证并确保令牌包含正确的aud字段,要么彻底移除config.Audience设置:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, config => { config.TokenValidationParameters = new TokenValidationParameters { ClockSkew = TimeSpan.FromMinutes(1), ValidateAudience = false }; config.Authority = "https://localhost:5001"; // 移除config.Audience = "NotesWebAPI"这一行 });
3. HTTPS重定向导致令牌丢失
后端配置了app.UseHttpsRedirection(),但客户端调用接口用的是HTTP地址http://localhost:1321。重定向到HTTPS时,请求头中的Bearer令牌会丢失,导致后端无法识别授权信息。
解决:
客户端改用后端的HTTPS地址调用接口:
var responce = await requestClient.GetAsync("https://localhost:xxxx/api/note"); // 替换为后端实际的HTTPS端口
内容的提问来源于stack exchange,提问作者Shy
相关产品推荐
相关产品推荐

