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

ASP.NET Core中JWT已授权但网页端仍返回401问题求助

问题排查与解决方案

核心问题

网页端访问返回401的根本原因是:登录后重定向到User控制器时,没有将JWT Token放入请求的Authorization头中。Postman是手动添加了Bearer Token头才通过认证,而你的代码仅将Token存入数据库,未让前端在后续请求中携带Token完成认证。

具体问题点分析

  1. 登录重定向逻辑错误
    登录控制器最后返回RedirectToAction("Index", "User", token),会把Token作为查询参数(如/User/Index?token=xxx)传递,但默认JWT Bearer认证只从Authorization请求头读取Token,不会处理查询参数,因此触发401。

  2. 前端缺少Token存储与携带逻辑
    网页端需要将登录获取的Token存储(如localStorage、HttpOnly Cookie),并在请求受保护资源时主动添加Authorization: Bearer {token}头。当前代码未将Token返回给前端存储,直接重定向导致Token丢失。

修复步骤

步骤1:修改登录控制器,返回Token给前端

将登录接口改为返回JSON格式的Token,让前端可获取并存储:

[HttpPost]
public IActionResult Index(string inputEmail, string inputPassword)
{
    var user = um.Get(x => x.Email == inputEmail);
    if (user == null)
    {
        return BadRequest("邮箱不存在");
    }

    if (!BCrypt.Net.BCrypt.Verify(inputPassword, user.PasswordHash))
    {
        return BadRequest("密码错误");
    }

    var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_authSettings.Value.Key));
    var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

    var jwtSecurityToken = new JwtSecurityToken(
                issuer: _authSettings.Value.Issuer,
                audience: _authSettings.Value.Audience,
                claims: null,
                expires: DateTime.Now.AddHours(1),
                notBefore: DateTime.Now,
                signingCredentials: credentials);

    var token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);

    user.Token = token;
    um.UserUpdate(user);    

    // 返回Token给前端,而非直接重定向
    return Ok(new { Token = token, Message = "登录成功" });
}

步骤2:前端处理Token存储与请求头添加

以原生JS为例,前端登录成功后存储Token并跳转:

// 登录请求示例
fetch('/Login/Index', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  body: `inputEmail=${encodeURIComponent(email)}&inputPassword=${encodeURIComponent(password)}`
})
.then(res => res.json())
.then(data => {
  if (data.Token) {
    localStorage.setItem('jwtToken', data.Token);
    // 跳转到User页面
    window.location.href = '/User/Index';
  }
});

后续访问受保护接口时,手动添加请求头:

fetch('/User/Index', {
  headers: {
    'Authorization': `Bearer ${localStorage.getItem('jwtToken')}`
  }
});

步骤3:(推荐)用HttpOnly Cookie存储Token(更安全)

如果是传统MVC项目,建议将Token存入HttpOnly Cookie避免XSS攻击:

  • 修改登录控制器添加Cookie:
// 生成Token后添加
Response.Cookies.Append("jwtToken", token, new CookieOptions
{
    HttpOnly = true,
    Secure = Environment.IsProduction(), // 生产环境启用HTTPS
    SameSite = SameSiteMode.Strict,
    Expires = DateTime.Now.AddHours(1)
});

return RedirectToAction("Index", "User");
  • 配置JwtBearer从Cookie读取Token:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(x =>
{
    x.TokenValidationParameters = new TokenValidationParameters
    {
        ValidIssuer = config["JwtSettings:Issuer"],
        ValidAudience = config["JwtSettings:Audience"],
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["JwtSettings:Key"])),
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
    };
    // 从Cookie读取Token
    x.Events = new JwtBearerEvents
    {
        OnMessageReceived = context =>
        {
            context.Token = context.Request.Cookies["jwtToken"];
            return Task.CompletedTask;
        }
    };
});

内容的提问来源于stack exchange,提问作者bart

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 16:13:25