使用Identity Server4在.NET微服务中遇鉴权问题:获取用户信息被禁止
1. 验证API的Audience与Token的Audience匹配
API服务必须配置正确的Audience,确保和IdentityServer4中定义的ApiResource名称一致。如果不匹配,Token会被判定为无效,直接返回403。
API启动类配置示例:
services.AddAuthentication("Bearer") .AddJwtBearer("Bearer", options => { options.Authority = "https://your-identity-server-url"; options.TokenValidationParameters = new TokenValidationParameters { ValidateAudience = true, ValidAudience = "your-api-resource-name", // 必须和IdentityServer中ApiResource的Name一致 ValidateIssuer = true, ValidIssuer = "https://your-identity-server-url" }; });
IdentityServer中ApiResource配置示例:
public static IEnumerable<ApiResource> GetApiResources() { return new List<ApiResource> { new ApiResource("your-api-resource-name", "Your API Display Name") { Scopes = { "your-api-scope" } } }; }
2. 确认登录请求包含必要的Scope
preferred_username属于profile scope,API调用需要对应的业务scope。如果登录时未请求这些scope,Token中会缺失相应Claim,导致403或无法获取用户名。
前端登录请求需包含的scope示例:
scope: "openid profile your-api-scope"
3. 检查IdentityServer的ProfileService实现
默认ProfileService不会自动返回所有用户Claim,需自定义实现IProfileService确保preferred_username被加入Token。
自定义ProfileService示例:
public class CustomProfileService : IProfileService { private readonly UserManager<ApplicationUser> _userManager; public CustomProfileService(UserManager<ApplicationUser> userManager) { _userManager = userManager; } public async Task GetProfileDataAsync(ProfileDataRequestContext context) { var user = await _userManager.GetUserAsync(context.Subject); if (user == null) throw new ArgumentException("Invalid user"); var claims = new List<Claim> { new Claim(JwtClaimTypes.PreferredUserName, user.UserName) // 可添加其他需要返回的Claim }; context.IssuedClaims.AddRange(claims); } public async Task IsActiveAsync(IsActiveContext context) { var user = await _userManager.GetUserAsync(context.Subject); context.IsActive = user != null; } }
在IdentityServer启动类中注册该服务:
services.AddScoped<IProfileService, CustomProfileService>();
4. 验证Client配置的Claim包含规则
在IdentityServer的Client配置中,需确保允许返回用户Claim到Token中,同时在AllowedScopes里包含profile。
Client配置示例:
public static IEnumerable<Client> GetClients() { return new List<Client> { new Client { ClientId = "your-client-id", ClientSecrets = { new Secret("your-client-secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, AllowedScopes = { "openid", "profile", "your-api-scope" }, AlwaysIncludeUserClaimsInIdToken = true, // 确保用户Claim被包含进Token AllowOfflineAccess = true } }; }
5. 检查API的授权策略
如果[Authorize]使用了自定义策略,需确保Token中的Claim满足策略要求。例如:
自定义策略配置:
services.AddAuthorization(options => { options.AddPolicy("RequirePreferredUserName", policy => policy.RequireClaim(JwtClaimTypes.PreferredUserName)); });
此时控制器需使用[Authorize(Policy = "RequirePreferredUserName")],若Token中无该Claim则返回403。
6. 确认使用正确的Token类型
调用API必须使用Access Token,而非Id Token。Id Token的受众是客户端,而非API,用它调用API会因Audience不匹配导致403。可通过JWT.io解析Token,查看aud字段确认是否为API的Audience。
内容的提问来源于stack exchange,提问作者mehdi

