.NET Core IdentityServer4资源所有者密码模式无法调用API
IdentityServer资源所有者密码模式认证问题解决(.NET Core WebAPI + React Native)
基于VS .NET Core WebAPI + Angular模板开发的Web应用,Angular前端的IdentityServer认证已配置正常,现需为React Native应用配置Resource Owner Password模式认证。目前能从IdentityServer获取access_token,但调用带[Authorize]属性的策略授权API时,服务器返回404 Not Found,实际为授权问题。
初始IdentityServer配置(Program.cs)
builder.Services.AddIdentityServer() .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options => { options.Clients[0].AllowOfflineAccess = true; options.Clients[0].AllowedScopes.Add(IdentityServerConstants.StandardScopes.OfflineAccess); options.Clients.Add(new Client { ClientId = "Willness.Mobile", ClientName = "Willness.Mobile", ClientSecrets = { new Duende.IdentityServer.Models.Secret("pwd".Sha256()) }, AllowedGrantTypes = { GrantType.ResourceOwnerPassword }, AllowedScopes = { "WillnessAPI", IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, IdentityServerConstants.StandardScopes.OfflineAccess }, AllowAccessTokensViaBrowser = true, AllowOfflineAccess = true }); }) .AddProfileService<ProfileService>();
排查发现的问题
- 通过Postman可成功获取token,但调用授权API时返回404(实际为授权失败)
- Angular应用的token经验证正常,Postman获取的token虽包含正确claims但存在异常
- 在AuthorizationHandler中,Angular请求的身份信息正常,Postman请求的身份信息为null
解决方案
1. 实现自定义ResourceOwnerPasswordValidator
public class ResourceOwnerPasswordValidator<T> : IResourceOwnerPasswordValidator where T : ApplicationIdentityUserBase { readonly UserManager<T> _userManager; readonly SignInManager<T> _signInManager; readonly IUserClaimsPrincipalFactory<T> _claimsFactory; public ResourceOwnerPasswordValidator(UserManager<T> userManager, SignInManager<T> signInManager, IUserClaimsPrincipalFactory<T> claimsFactory) { _userManager = userManager; _signInManager = signInManager; _claimsFactory = claimsFactory; } public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context) { var username = context.UserName; var password = context.Password; var user = await _userManager.FindByNameAsync(username) ?? await _userManager.FindByEmailAsync(username); if (user != null && !user.Deleted) { var result = await _signInManager.PasswordSignInAsync(user.UserName, password, false, false); if (result.Succeeded) { var principal = await _claimsFactory.CreateAsync(user); var claims = principal.Claims.Distinct(new ClaimComparer()).ToList(); var claimsInDb = await _userManager.GetClaimsAsync(user); foreach (var claim in claimsInDb) { if (!claims.Any(c => c.Type == claim.Type && c.Value == claim.Value)) claims.Add(claim); } // 标记验证成功 context.Result = new GrantValidationResult( subject: user.Id, authenticationMethod: GrantType.ResourceOwnerPassword, claims: claims); return; } } // 标记验证失败 context.Result = new GrantValidationResult( TokenRequestErrors.UnauthorizedClient, "Invalid credentials"); } }
2. 在Program.cs中注册自定义验证器
在IdentityServer配置中添加自定义验证器的注册:
builder.Services.AddIdentityServer() .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options => { options.Clients[0].AllowOfflineAccess = true; options.Clients[0].AllowedScopes.Add(IdentityServerConstants.StandardScopes.OfflineAccess); options.Clients.Add(new Client { ClientId = "Willness.Mobile", ClientName = "Willness.Mobile", ClientSecrets = { new Duende.IdentityServer.Models.Secret("pwd".Sha256()) }, AllowedGrantTypes = { GrantType.ResourceOwnerPassword }, AllowedScopes = { "WillnessAPI", IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, IdentityServerConstants.StandardScopes.OfflineAccess }, AllowAccessTokensViaBrowser = true, AllowOfflineAccess = true }); }) .AddResourceOwnerValidator<ResourceOwnerPasswordValidator<ApplicationUser>>() // 新增行 .AddProfileService<ProfileService>();
3. 确保ProfileService的IsActiveAsync方法返回正确值
修改ProfileService中的IsActiveAsync方法,确保正确标记用户状态为活跃:
public async Task IsActiveAsync(IsActiveContext context) { var sub = context.Subject.GetSubjectId(); var user = await _userManager.FindByIdAsync(sub) ?? await _userManager.FindByNameAsync(sub); context.IsActive = user != null; }
配置完成后,可获取有效access_token并成功调用授权API,实现IdentityServer的ResourceOwnerPassword模式认证。
内容的提问来源于stack exchange,提问作者Androidian
相关产品推荐
相关产品推荐

