在 ASP.NET 核心中创建 JWT 是不是有更简单的方法?又名“JWT 身份验证的最佳实践?”
Posted
技术标签:
【中文标题】在 ASP.NET 核心中创建 JWT 是不是有更简单的方法?又名“JWT 身份验证的最佳实践?”【英文标题】:Is there a simpler way for creating JWTs in ASP.NET core? aka “Best Practices for JWT Authentication?”在 ASP.NET 核心中创建 JWT 是否有更简单的方法?又名“JWT 身份验证的最佳实践?” 【发布时间】:2021-10-23 22:38:54 【问题描述】:.NET5 拥有大量用于身份验证的中间件和基础设施。您可以包含一些 NuGet 包,在 Startup.cs 中连接一些配置,并且您的应用程序可以理解 cookie 和会话。我在网上为基于 JWT 的身份验证找到的所有示例都让我手动实例化 JwtSecurityDescriptor
并在其中设置属性。这真的感觉像是应该成为SignInManager.PasswordSignInAsync
流程的一部分。例如:
-
https://www.faciletechnolab.com/Blog/2021/4/5/how-to-implement-jwt-token-authentication-in-aspnet-core-50-web-api-using-jwt
https://weblog.west-wind.com/posts/2021/Mar/09/Role-based-JWT-Tokens-in-ASPNET-Core
http://www.binaryintellect.net/articles/1fdc8b3f-06a1-4f36-8c0b-7852bf850f52.aspx
我错过了什么吗?这是在线示例跳过几个步骤以支持“简化”演示的情况吗?前面的每个示例都手动创建 JWT,但将其大量解释委托给中间件。
为了澄清,我不是在寻找有关如何验证密码的说明(我使用 IPasswordHasher
执行此操作)或定位用户(我使用 IUserPasswordStore
执行此操作)。我特别想知道我是否缺少一些东西来导致 ASP.NET 为我处理 JWT 生成工作流。我不知道在线示例是否省略了很多“用于演示”的功能(类似于许多在线指南如何将数据库调用直接放在控制器中以实现“简单性”)。
【问题讨论】:
【参考方案1】:一般来说,当我们使用 JWT 认证时,工作流程如下:
-
客户端向服务器发送请求(包含用户信息,如:用户名和密码)获取token
服务器接收用户信息并检查授权。如果验证成功,服务器会生成一个 JWT 令牌。
客户端收到令牌并将其存储在本地某处。
客户端在以后的请求中发送令牌。
服务器从请求标头中获取令牌,再次计算哈希,方法是使用 a) 令牌中的标头 b) 令牌中的有效负载 c) 服务器已经拥有的密钥。
如果 ("newly computed hash" = "hash come in token"),则 token 有效,否则它被调和或无效
您可以参考以下步骤在 ASP.NET Core 中使用 JWT 身份验证。
使用 JWT 承载选项配置身份验证架构。
public void ConfigureServices(IServiceCollection services)
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:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
;
);
services.AddControllersWithViews();
在本例中,我将这些值存储在 appsettings.json 文件中。
"Jwt":
"Key": "ThisismySecretKey",
"Issuer": "Test.com"
在启动类的Configure方法中调用app.UseAuthentication()方法。
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
endpoints.MapControllerRoute(
name: "default",
pattern: "controller=Home/action=Index/id?");
);
生成 JSON 网络令牌
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using Test.Models;
namespace Test.Controllers
[Route("api/[controller]")]
[ApiController]
public class LoginController : ControllerBase
private IConfiguration _config;
public LoginController(IConfiguration config)
_config = config;
[AllowAnonymous]
[HttpPost]
public IActionResult Login([FromBody] UserModel login)
IActionResult response = Unauthorized();
var user = AuthenticateUser(login);
if (user != null)
var tokenString = GenerateJSONWebToken(user);
response = Ok(new token = tokenString );
return response;
private string GenerateJSONWebToken(UserModel userInfo)
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var claims = new[]
new Claim(JwtRegisteredClaimNames.Sub, userInfo.Username),
new Claim(JwtRegisteredClaimNames.Email, userInfo.EmailAddress),
new Claim("DateOfJoing", userInfo.DateOfJoing.ToString("yyyy-MM-dd")),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
;
var token = new JwtSecurityToken(_config["Jwt:Issuer"],
_config["Jwt:Issuer"],
claims,
expires: DateTime.Now.AddMinutes(120),
signingCredentials: credentials);
return new JwtSecurityTokenHandler().WriteToken(token);
private UserModel AuthenticateUser(UserModel login)
UserModel user = null;
//Validate the User Credentials
//Demo Purpose, I have Passed HardCoded User Information
if (login.Username == "Jignesh")
user = new UserModel Username = "Jignesh Trivedi", EmailAddress = "test.btest@gmail.com" ;
return user;
那么,如果你请求“API/login”方法生成令牌,你必须在请求正文中传递以下JSON。
"username": "Jignesh", "password": "password"
然后,在获取 JWT 令牌后,您可以在访问其他 API 控制器时在请求头中添加“授权”属性(带有[Authorize]
属性)。
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJKaWduZXNoIFRyaXZlZGkiLCJlbWFpbCI6InRlc3QuYnRlc3RAZ21haWwuY29tIiwiRGF0ZU9mSm9pbmciOiIwMDAxLTAxLTAxIiwianRpIjoiYzJkNTZjNzQtZTc3Yy00ZmUxLTgyYzAtMzlhYjhmNzFmYzUzIiwiZXhwIjoxNTMyMzU2NjY5LCJpc3MiOiJUZXN0LmNvbSIsImF1ZCI6IlRlc3QuY29tIn0.8hwQ3H9V8mdNYrFZSjbCpWSyR1CNyDYHcGf6GqqCGnY
更多详细信息,请参考以下链接:
JWT Authentication In ASP.NET Core
JWT Validation on separate web Api
【讨论】:
感谢您的回答,非常完整且有帮助。但是,它仍然显示了我遇到的问题。您正在控制器操作中手动生成 JWT。这是正确的地方吗,或者这只是一个演示?作为 SignInManager.SignInAsync 工作流的一部分,为什么没有在某种 Microsoft.Identity 对象内部生成 JWT? 本教程和示例是一个基本教程,告诉我们JWT是如何工作的以及如何在我们的应用程序中使用它,您也可以创建custom middleware to handle the JWT authentication。那么,如果你使用 IdentityServer 或者外部提供者认证,JWT 令牌是由外部提供者生成的,拿到令牌后,你可以按照上面的教程使用它。以上是关于在 ASP.NET 核心中创建 JWT 是不是有更简单的方法?又名“JWT 身份验证的最佳实践?”的主要内容,如果未能解决你的问题,请参考以下文章
控制器 ASP.net 核心 2.1 中的 Jwt 角色身份验证