ASP.NET MVC中JWT与Cookie结合实现认证的问题求助
问题解决步骤
1. 修复SignInAsync的错误
你遇到的错误是因为创建的ClaimsPrincipal未被标记为已认证状态,解决方式有两种:
方案1:规范添加用户标识Claim(推荐)
修改AccountController中的登录代码,添加用户唯一标识Claim,确保身份被正确标记为已认证:
if (login.Password.Equals(passwordCheck) && login.Username.Equals(adminCheck)) { var claims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier, adminCheck), // 添加用户唯一标识 new Claim(ClaimTypes.Role, "Admin") }; var claimsIdentity = new ClaimsIdentity( claims, CookieAuthenticationDefaults.AuthenticationScheme); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity)); return RedirectToAction("Index", "User"); }
方案2:关闭Cookie认证的强制已认证检查
如果不想添加额外Claim,可在Cookie认证配置中关闭RequireAuthenticatedSignIn:
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => { options.LoginPath = "/Account/Login"; options.AccessDeniedPath = "/Account/AccessDenied"; options.ExpireTimeSpan = TimeSpan.FromMinutes(30); options.RequireAuthenticatedSignIn = false; // 关闭强制检查 })
2. JWT Token的存储选择
两种存储方式各有适用场景,你的MVC项目可结合使用:
- Cookie存储:适合传统页面跳转,浏览器自动携带,安全性更高(可设置
HttpOnly/Secure/SameSite属性防范XSS和CSRF)。 - 请求头
Authorization:适合Ajax/API调用,需前端手动存储(如localStorage)并携带,灵活性更强,但需注意XSS风险。
3. 整合JWT生成到登录流程
登录成功后同时生成Cookie会话和JWT Token,兼顾页面跳转和Ajax需求:
if (login.Password.Equals(passwordCheck) && login.Username.Equals(adminCheck)) { var claims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier, adminCheck), new Claim(ClaimTypes.Role, "Admin") }; // 生成JWT Token var key = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(builder.Configuration.GetValue<string>("JwtSettings:SecretKey"))); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( issuer: builder.Configuration.GetValue<string>("JwtSettings:Issuer"), audience: builder.Configuration.GetValue<string>("JwtSettings:Audience"), claims: claims, expires: DateTime.Now.AddMinutes(30), signingCredentials: creds); var jwtToken = new JwtSecurityTokenHandler().WriteToken(token); // 方案1:将JWT存入HttpOnly Cookie(安全防XSS) Response.Cookies.Append("JwtToken", jwtToken, new CookieOptions { HttpOnly = true, Secure = true, // 生产环境开启,仅HTTPS传输 SameSite = SameSiteMode.Strict, Expires = DateTime.Now.AddMinutes(30) }); // 方案2:返回JWT给前端存入localStorage(适合Ajax) // return Json(new { token = jwtToken }); // 执行Cookie认证,支持页面跳转 var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity)); return RedirectToAction("Index", "User"); }
4. 配置双认证方案支持
修改Program.cs,让系统同时支持Cookie和JWT认证:
builder.Services.AddAuthentication() .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => { options.LoginPath = "/Account/Login"; options.AccessDeniedPath = "/Account/AccessDenied"; options.ExpireTimeSpan = TimeSpan.FromMinutes(30); }) .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, x => { x.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = builder.Configuration.GetValue<string>("JwtSettings:Issuer"), ValidateAudience = true, ValidAudience = builder.Configuration.GetValue<string>("JwtSettings:Audience"), ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(builder.Configuration.GetValue<string>("JwtSettings:SecretKey"))), ValidateLifetime = true, ClockSkew = TimeSpan.FromSeconds(300), RequireExpirationTime = true }; // 配置从Cookie读取JWT Token(如果用Cookie存储方案) x.Events = new JwtBearerEvents { OnMessageReceived = context => { context.Token = context.Request.Cookies["JwtToken"]; return Task.CompletedTask; } }; }); // 设置默认授权策略,允许两种认证方式 builder.Services.AddAuthorization(options => { options.DefaultPolicy = new AuthorizationPolicyBuilder( CookieAuthenticationDefaults.AuthenticationScheme, JwtBearerDefaults.AuthenticationScheme) .RequireAuthenticatedUser() .Build(); });
5. Ajax调用的JWT认证实现
场景1:JWT存在localStorage
前端手动携带Token到请求头:
// 登录成功后存储Token到localStorage(需后端返回Token) // localStorage.setItem('jwtToken', 后端返回的token); $.ajax({ url: '/Product/GetActivationCode', type: 'GET', headers: { 'Authorization': 'Bearer ' + localStorage.getItem('jwtToken') }, data: { id: id }, success: function (response) { $("#actCodeText").text("Activation Code: " + response); }, error: function (xhr, status, error) { console.error(error); } });
场景2:JWT存在HttpOnly Cookie
无需手动设置请求头,浏览器自动携带Cookie,后端通过JwtBearer的OnMessageReceived事件读取Token即可。
6. 保护Controller Action
在需要认证的Action上添加[Authorize]特性:
[Authorize] [HttpGet] public async Task<string> GetActivationCode(string id) { var actCode = (await _productService.GetProduct(id)).ActivationCode; return actCode; }
若需指定单一认证方案,可写:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
内容的提问来源于stack exchange,提问作者jett
相关产品推荐
相关产品推荐

