You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
    }
});

无需手动设置请求头,浏览器自动携带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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 05:20:58