Identity Server 4使用数据库用户异步请求密码令牌异常问题咨询
问题定位与修复方案
1. 配置顺序错误
当前Startup中服务注册顺序错误,AddIdentity必须放在AddIdentityServer之前,否则AddAspNetIdentity<ApplicationUser>无法正常依赖已注册的Identity服务,正确顺序如下:
// 先注册ASP.NET Core Identity services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<DataContext>() .AddDefaultTokenProviders(); // 再注册IdentityServer services.AddIdentityServer() //Configuration Store: clients and resources .AddConfigurationStore(options => { options.ConfigureDbContext = builder => builder.UseMySQL(connectionString, opt => opt.MigrationsAssembly(migrationAssembly)); }) //Oprerationals Store: tokens, consets, codes, etc .AddOperationalStore(options => { options.ConfigureDbContext = builder => builder.UseMySQL(connectionString, opt => opt.MigrationsAssembly(migrationAssembly)); }) .AddAspNetIdentity<ApplicationUser>() .AddDeveloperSigningCredential();
2. 自定义用户模型属性冲突
你继承了IdentityUser但重复定义了Id、Email、UserName、PasswordHash等父类已有的属性,且自定义了Id为int类型,需要修改为泛型版本的父类继承,删除重复属性定义:
// 用IdentityUser<int>指定主键类型为int,仅保留自定义扩展字段 public class ApplicationUser : IdentityUser<int> { public string Name { get; set; } public string AvatarUrl { get; set; } }
对应你的DataContext需要改为继承IdentityDbContext<ApplicationUser, IdentityRole<int>, int>,保证主键类型全局统一。
3. 自定义密码哈希和Identity体系不兼容
你自己实现的CreatePasswordHash、VerifyPasswordHash用的是自定义哈希规则,而IS4集成AddAspNetIdentity后,密码校验走ASP.NET Core Identity自带的哈希逻辑,两者不兼容导致密码校验永远失败,需要修改注册、登录逻辑复用Identity自带能力:
注册逻辑修改
// 注入UserManager<ApplicationUser>替代手动操作DbContext public async Task<ServiceResponse<int>> Register(ApplicationUser user, string password) { ServiceResponse<int> response = new ServiceResponse<int>(); if (await UserExists(user.Email)) { response.Success = false; response.Message = "User already exists."; return response; } user.UserName = user.Email; // 用UserManager自动处理密码哈希、用户存储 var createResult = await _userManager.CreateAsync(user, password); if (!createResult.Succeeded) { response.Success = false; response.Message = string.Join(";", createResult.Errors.Select(x => x.Description)); return response; } response.Data = user.Id; return response; }
登录逻辑修改
public async Task<ServiceResponse<string>> Login(string email, string password) { var response = new ServiceResponse<string>(); var user = await _userManager.FindByEmailAsync(email); if (user == null) { response.Success = false; response.Message = "User not found."; } else if (!await _userManager.CheckPasswordAsync(user, password)) { response.Success = false; response.Message = "Wrong password."; } else { response.Data = await CreateToken(email, password); } return response; }
4. Config配置与持久化问题
你已经配置了AddConfigurationStore将客户端、资源等配置持久化到数据库,需要执行初始化逻辑把Config类中的IdentityResources、ApiScopes、ApiResources、Clients写入数据库,否则IS4读取不到配置会校验失败。另外你已经集成了ASP.NET Identity,不需要再保留GetUsers返回TestUser的代码,可以直接删除。
5. Token生成逻辑优化
不需要在服务端内部发起HTTP请求调用IS4的Token端点,直接注入IS4的ITokenService在内存生成Token即可,减少不必要的网络开销。
适配确认
不需要完全重写注册登录逻辑,仅需要将原有自定义数据库操作替换为Identity自带的UserManager、SignInManager相关方法即可适配IS4;另外需要确保数据库已经执行了Identity和IS4的迁移,生成了对应的用户表、配置表、操作存储表。
注:资源所有者密码流官方不推荐用于生产环境,生产环境建议改用Authorization Code流
内容的提问来源于stack exchange,提问作者Adrian Tocu

