ASP.NET Core WebApi中如何禁用SignInManager的Cookie仅用JWTBearer
没问题,我来帮你搞定这个问题——在ASP.NET Core WebApi中结合Identity和JWT Bearer认证,同时避免SignInManager生成Cookie,核心是调整Identity的配置并替换默认的登录逻辑,具体步骤如下:
1. 调整Identity配置,从根源禁用默认Cookie认证
默认情况下,AddIdentity会自动注册Cookie认证方案,这就是SignInManager生成Cookie的原因。我们可以改用更轻量的AddIdentityCore来配置Identity,它不会自动绑定Cookie认证,完美适配WebApi场景:
services.AddIdentityCore<ApplicationUser>(options => { // 这里配置你的Identity规则,比如密码复杂度、用户名验证等 options.Password.RequireDigit = true; options.Password.RequireLowercase = true; // ...其他自定义配置 }) .AddRoles<IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddSignInManager() // 保留SignInManager,但它不会默认关联Cookie .AddDefaultTokenProviders();
如果你已经在使用AddIdentity,也可以通过修改Cookie配置来禁用它:
services.AddIdentity<ApplicationUser, IdentityRole>(options => { // 你的Identity配置 }) .AddEntityFrameworkStores<ApplicationDbContext>() .AddSignInManager() .AddDefaultTokenProviders(); // 禁用Cookie生成或调整其行为 services.ConfigureApplicationCookie(options => { // 对于WebApi,我们不需要重定向到登录页,直接返回401 options.Events.OnRedirectToLogin = context => { context.Response.StatusCode = StatusCodes.Status401Unauthorized; return Task.CompletedTask; }; // 或者直接设置空Cookie名称,阻止Cookie生成 options.Cookie.Name = string.Empty; });
2. 替换SignInManager.PasswordSignInAsync为手动验证+JWT生成
PasswordSignInAsync的设计初衷就是为Cookie认证创建会话,所以即使禁用了Cookie,这个方法可能仍会尝试生成无效的Cookie。更好的方式是直接用UserManager验证密码,然后手动生成JWT令牌返回给客户端:
先确保JWT Bearer认证已正确配置
在ConfigureServices中添加JWT Bearer的配置:
services.AddAuthentication(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"])) }; });
在登录接口中实现手动验证和JWT生成
不再调用SignInManager.PasswordSignInAsync,而是用UserManager.CheckPasswordAsync验证用户凭证,然后生成JWT返回:
[ApiController] [Route("api/auth")] public class AuthController : ControllerBase { private readonly UserManager<ApplicationUser> _userManager; private readonly IConfiguration _configuration; public AuthController(UserManager<ApplicationUser> userManager, IConfiguration configuration) { _userManager = userManager; _configuration = configuration; } [HttpPost("login")] public async Task<IActionResult> Login([FromBody] LoginModel model) { var user = await _userManager.FindByNameAsync(model.Username); if (user != null && await _userManager.CheckPasswordAsync(user, model.Password)) { // 构建JWT声明 var authClaims = new List<Claim> { new Claim(ClaimTypes.Name, user.UserName), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), // 按需添加用户角色等其他声明 }; var userRoles = await _userManager.GetRolesAsync(user); foreach (var role in userRoles) { authClaims.Add(new Claim(ClaimTypes.Role, role)); } // 生成JWT令牌 var authSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"])); var token = new JwtSecurityToken( issuer: _configuration["Jwt:Issuer"], audience: _configuration["Jwt:Audience"], expires: DateTime.Now.AddHours(3), claims: authClaims, signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256) ); return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token), expiration = token.ValidTo }); } return Unauthorized("用户名或密码错误"); } }
这样处理后,登录接口只会返回JWT令牌,不会生成任何Cookie,完全符合WebApi的无状态认证需求。
3. 确保中间件顺序正确
在Configure方法中,一定要保证UseAuthentication在UseAuthorization之前,这样请求会先完成认证再进行授权检查:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // ...其他中间件(比如异常处理、静态文件等) app.UseRouting(); app.UseAuthentication(); // 先执行认证 app.UseAuthorization(); // 再执行授权 app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
完成以上配置后,你的WebApi就只会通过请求头中的Authorization: Bearer {你的JWT令牌}来验证身份,再也不会生成Cookie了。
内容的提问来源于stack exchange,提问作者PeterMacko

