提问人:micex 提问时间:11/12/2023 最后编辑:micex 更新时间:11/21/2023 访问量:171
在 ASP.NET Core MVC 中使用 JWT 进行身份验证时出现问题
Issue with Using JWT for Authentication in ASP.NET Core MVC
问:
我在 ASP.NET Core MVC 中使用 JSON Web 令牌 (JWT) 实现身份验证时遇到了问题。我有一个 ASP.NET Core API,其中包含一个登录端点,该端点返回 JWT 以响应用户登录。以下是我在 API 中的登录端点的代码:
[HttpPost]
[Route("login")]
public async Task<IActionResult> Login([FromBody] LoginUserDTO userDTO)
{
// ... (omission for brevity)
return Accepted(new TokenRequest { Token = await _authManager.CreateToken(), RefreshToken = await _authManager.CreateRefreshToken() });
}
public async Task<string> CreateToken()
{
var signingCredentials = GetSigningCredentials();
var claims = await GetClaims();
var token = GenerateTokenOptions(signingCredentials, claims);
return new JwtSecurityTokenHandler().WriteToken(token);
}
private JwtSecurityToken GenerateTokenOptions(SigningCredentials signingCredentials, List<Claim> claims)
{
var jwtSettings = _configuration.GetSection("Jwt");
var expiration = DateTime.Now.AddMinutes(Convert.ToDouble(
jwtSettings.GetSection("lifetime").Value));
var token = new JwtSecurityToken(
issuer: jwtSettings.GetSection("Issuer").Value,
claims: claims,
expires: expiration,
signingCredentials: signingCredentials
);
return token;
}
private async Task<List<Claim>> GetClaims()
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, _user.UserName)
};
var roles = await _userManager.GetRolesAsync(_user);
foreach (var role in roles)
{
claims.Add(new Claim(ClaimTypes.Role, role));
}
return claims;
}
private SigningCredentials GetSigningCredentials()
{
var key = _configuration.GetSection("Jwt:KEY").Value;
var secret = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
return new SigningCredentials(secret, SecurityAlgorithms.HmacSha256);
}
现在,在我的 MVC 项目中,我有一个 Login 方法,它调用 API 进行登录并安全地保存令牌:
public async Task<IActionResult> Login(LoginViewModel model)
{
// ... (omission for brevity)
var token = await GetAccessToken(model); // call tha API to get the token
if (!string.IsNullOrEmpty(token))
{
SaveToken(token);
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", "Invalid username or password");
}
return View(model);
}
private void SaveTokenSecurely(string token)
{
// Puoi salvare il token in un cookie sicuro, ad esempio:
var cookieOptions = new CookieOptions
{
HttpOnly = true,
Secure = true,
SameSite = SameSiteMode.Strict,
Expires = DateTime.UtcNow.AddHours(1)
};
Response.Cookies.Append("AccessToken", token, cookieOptions);
}
以下是我在 MVC 项目中配置身份验证的方法:
builder.Services.AddAuthentication(auth =>
{
auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
auth.DefaultForbidScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
var jwtSettings = builder.Configuration.GetSection("Jwt");
var key = jwtSettings.GetSection("Key").Value;
var issuer = jwtSettings.GetSection("Issuer").Value;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = false,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = issuer,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)),
};
});
API 和 MVC 项目的 appsettings.json 文件相同:The appsettings.json file is the same for the API and MVC project:
"Jwt": {
"Issuer": "MY.PROJECT.Api",
"lifetime": 15,
"Key": "SUPERR STRONG KEY"
}
问题是,尽管成功登录并获取了令牌,但当我尝试使用 [Authorize(Roles = “Administrator”)] 属性访问我的隐私页面时,我仍然收到 401 错误。我已验证令牌的有效负载中是否存在“管理员”角色。
我在 jwt.io 检查了令牌,结果如下。如果未选中,则收到无效签名。secret base64 encoded
我还尝试从Postman呼叫设置> >,这按预期工作。Privacy()
Authorization
Bearer Token
and then the token
我知道 Stack Overflow 上有几个关于 ASP.NET Core MVC 中 JWT 令牌未经授权的错误的问题,我已经研究了其中的一些问题:
- JWT 令牌错误 401 在 .NET Core 3.1 中未经授权
- ASP.NET 核心 JWT 身份验证总是抛出 401 未经授权
- 获得 401 未经授权的 ASP.NET Core 6
- ASP.NET Core 5 和 6 JWT 身份验证始终抛出 HTTP 401 代码
有没有人知道问题可能是什么或我如何解决它?提前感谢您的帮助!
临时解决方案
我解决了它,添加了这个:
builder.Services.AddAuthentication(auth =>
{
auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
auth.DefaultForbidScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
var jwtSettings = builder.Configuration.GetSection("Jwt");
var key = jwtSettings.GetSection("Key").Value;
var issuer = jwtSettings.GetSection("Issuer").Value;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = false,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = issuer,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)),
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var token = context.Request.Cookies["AccessToken"];
context.Token = token;
return Task.CompletedTask;
}
};
});
虽然这不是一个很好的解决方案。您能否就我的问题提供完整的答案?
答:
我认为@DerDingens(在评论中)是对的,总结一下:
- 如果 HTTP 请求不包含标头,这将永远不会起作用:
'Authorization: Bearer <JWT_TOKEN>'
因为这就是 JWT 的工作方式:“
JWT auth”->表示“授权:持有者......”标头必须存在
,如果您使用 cookie,则:
'Cookie' Auth -> Cookie 必须存在
- 在您的特定情况下(将 JWT 存储在 cookie 中): 我想我在网上看到过几次类似的问题, 您可以使用下面的代码来解决此问题。它与您的解决方案非常相似。
解决方案:添加中间件,在 UseAuth 之前设置标头“Authorization: Bearer”。中间件,例如:
// Add this middleware before authorization
// obviously 'AccessToken' is Your cookie name
app.Use(async (context, next) =>
{
var token = context.Request.Cookies["AccessToken"];
if (!string.IsNullOrEmpty(token) &&
!context.Request.Headers.ContainsKey("Authorization"))
{
context.Request.Headers.Add("Authorization", "Bearer " + token);
}
await next();
});
评论
.NET7 简化了这一点。
using Microsoft.AspNetCore.Authentication.JwtBearer;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication().AddJwtBearer(); //👈 new feature
builder.Services.AddAuthorization();
var app = builder.Build();
https://auth0.com/blog/whats-new-in-dotnet-7-for-authentication-and-authorization/
尝试尽可能使用 Microsoft 默认值,以便像工作这样的东西dotnet user-jwts
评论
options.Events = new JwtBearerEvents{ OnMessageReceived = context => { var token = context.Request.Cookies["AccessToken"]; context.Token = token; return Task.CompletedTask; }};