能否将ASP.NET Core Identity与JWT结合?启动报错求解决
问题解决:IEmailSender未注册错误 + 替换ASP.NET Core Identity默认令牌为JWT
一、先解决IEmailSender未注册的启动错误
MapIdentityApi<User>底层依赖IEmailSender<TUser>服务处理邮箱验证、密码重置这类流程,你没注册该服务才触发了InvalidOperationException。有两种处理方式:
1. 快速临时解决:注册空实现
如果暂时不需要邮件功能,可注册一个空实现绕过报错:
// 在服务扩展或Program.cs中添加注册代码 services.AddScoped<IEmailSender<YourUserType>, NullEmailSender>(); // 定义空实现类 public class NullEmailSender : IEmailSender<YourUserType> { public Task SendConfirmationLinkAsync(YourUserType user, string email, string confirmationLink) { return Task.CompletedTask; } public Task SendPasswordResetLinkAsync(YourUserType user, string email, string resetLink) { return Task.CompletedTask; } public Task SendPasswordResetCodeAsync(YourUserType user, string email, string resetCode) { return Task.CompletedTask; } }
2. 正式方案:实现自定义邮件发送服务
如果后续需要邮箱验证等功能,可基于IEmailSender<YourUserType>实现自己的邮件逻辑(比如用SMTP或第三方邮件服务),再注册到DI容器:
// 自定义邮件发送实现 public class CustomEmailSender : IEmailSender<YourUserType> { private readonly IConfiguration _config; public CustomEmailSender(IConfiguration config) { _config = config; } public async Task SendConfirmationLinkAsync(YourUserType user, string email, string confirmationLink) { // 示例:用SMTP发送确认邮件 using var smtpClient = new SmtpClient(_config["Smtp:Host"]) { Port = int.Parse(_config["Smtp:Port"]), Credentials = new NetworkCredential(_config["Smtp:Username"], _config["Smtp:Password"]), EnableSsl = true }; var message = new MailMessage( from: _config["Smtp:From"], to: email, subject: "邮箱确认", body: $"点击链接完成确认:{confirmationLink}" ); await smtpClient.SendMailAsync(message); } public Task SendPasswordResetLinkAsync(YourUserType user, string email, string resetLink) { // 实现密码重置链接发送逻辑 return Task.CompletedTask; } public Task SendPasswordResetCodeAsync(YourUserType user, string email, string resetCode) { // 实现密码重置验证码发送逻辑 return Task.CompletedTask; } } // 注册到服务容器 services.AddScoped<IEmailSender<YourUserType>, CustomEmailSender>();
二、替换默认Bearer令牌为JWT
解决报错后,按以下步骤配置JWT替换Identity的默认文本令牌:
1. 添加JWT配置到appsettings.json
"Jwt": { "Issuer": "YourApiIssuer", "Audience": "YourApiAudience", "Key": "YourSuperSecretKeyAtLeast16CharactersLong" // 生产环境建议从环境变量读取安全密钥 }
2. 在服务扩展中配置JWT认证
// 在AddIdentityCore之后添加JWT认证服务 services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = configuration["Jwt:Issuer"], ValidAudience = configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Jwt:Key"])) }; }); // 确保Identity服务配置正确 services.AddIdentityCore<YourUserType>(options => { // 可自定义密码、用户验证规则 options.Password.RequireDigit = true; options.Password.RequireLowercase = true; }) .AddEntityFrameworkStores<YourDbContext>() .AddApiEndpoints(); // 必须添加,支持MapIdentityApi
3. 调整中间件顺序(关键)
中间件顺序直接影响功能生效,确保UseAuthentication在UseAuthorization和MapIdentityApi之前:
var app = builder.Build(); app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthentication(); // 先执行认证 app.UseAuthorization(); // 再执行授权 // 映射Identity的API端点 app.MapIdentityApi<YourUserType>(); // 映射自定义API控制器 app.MapControllers(); app.Run();
4. 自定义登录接口返回JWT(可选)
默认/login端点返回Identity原生令牌,若要直接返回JWT,可自定义登录接口:
app.MapPost("/api/auth/login", async (LoginRequest request, SignInManager<YourUserType> signInManager, IConfiguration config) => { var user = await signInManager.UserManager.FindByEmailAsync(request.Email); if (user == null) return Results.Unauthorized(); var signInResult = await signInManager.CheckPasswordSignInAsync(user, request.Password, lockoutOnFailure: false); if (!signInResult.Succeeded) return Results.Unauthorized(); // 构建JWT声明 var claims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), new Claim(ClaimTypes.Email, user.Email) // 添加其他业务所需声明 }; // 生成JWT令牌 var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["Jwt:Key"])); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( issuer: config["Jwt:Issuer"], audience: config["Jwt:Audience"], claims: claims, expires: DateTime.Now.AddHours(1), signingCredentials: creds ); return Results.Ok(new { Token = new JwtSecurityTokenHandler().WriteToken(token) }); }) .WithName("Login") .WithOpenApi(); // 登录请求模型 public class LoginRequest { public string Email { get; set; } public string Password { get; set; } }
内容的提问来源于stack exchange,提问作者nicolascanala
相关产品推荐
相关产品推荐

