.NET 实现 JWT 登录验证

  • .NET 实现 JWT 登录验证已关闭评论
  • 168 次浏览
  • A+
所属分类:.NET技术
摘要

在ASP.NET Core应用程序中,使用JWT进行身份验证和授权已成为一种流行的方式。JWT是一种安全的方式,用于在客户端和服务器之间传输用户信息。


.NET 实现JWT登录认证

在ASP.NET Core应用程序中,使用JWT进行身份验证和授权已成为一种流行的方式。JWT是一种安全的方式,用于在客户端和服务器之间传输用户信息。

添加NuGet包

首先,我们需要添加一些NuGet包来支持JWT身份验证。在您的ASP.NET Core项目中,打开Startup.cs文件,并在ConfigureServices方法中添加以下代码:

using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; 

注册

// ...  public void ConfigureServices(IServiceCollection services) {     // 添加JWT身份验证服务     services.AddAuthentication(options =>     {         options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;         options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;     })     .AddJwtBearer(options =>     {         var config = Configuration.GetSection("JwtConfig").Get<JwtConfig>(); // 从appsettings.json读取JwtConfig配置         options.TokenValidationParameters = new TokenValidationParameters         {             ValidateIssuer = true,             ValidateAudience = true,             ValidateLifetime = true,             ValidateIssuerSigningKey = true,             ValidIssuer = config.Issuer,             ValidAudience = config.Audience,             IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config.SecretKey))         };                  options.Events = new JwtBearerEvents         {             OnMessageReceived = async context =>             {                 var token = context.Request.Cookies["access_token"]; // 从Cookie中获取token值                 if (!string.IsNullOrEmpty(token))                 {                     context.Token = token; // 将token值设置到JwtBearer上下文中的Token属性                 }             }         };     });      // ... } 

这里我们使用AddAuthentication方法添加了JWT身份验证服务,并设置了默认的认证方案为JwtBearerDefaults.AuthenticationScheme,这是JWT身份验证的默认方案。

AddJwtBearer方法中,我们通过GetSection方法从appsettings.json文件中读取了一个名为JwtConfig的配置,其中包含了JWT的一些信息,例如签发者(Issuer)、接收者(Audience)、秘钥(SecretKey)等。这些信息将用于验证和生成JWT令牌。

Configure方法中添加JWT认证中间件:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {     // 其他中间件配置...      app.UseAuthentication();     app.UseAuthorization();    //... } 

配置appsettings.json

接下来,我们需要在appsettings.json文件中配置JWT的相关信息。在您的ASP.NET Core项目中,找到appsettings.json文件,并添加以下配置:

{   // ...   "JwtConfig": {     "Issuer": "YourIssuer",     "Audience": "YourAudience",     "SecretKey": "YourSecretKey",     "AccessTokenExpirationMinutes": 60,     "RefreshTokenExpirationMinutes": 1440   } } 

您可以根据自己的需求修改配置项的值。这里的AccessTokenExpirationMinutesRefreshTokenExpirationMinutes分别表示访问令牌和刷新令牌的过期时间,单位为分钟。

创建 JWT 设置类

接下来,我们需要创建一个 C# 类来表示 JWT 的配置项,并使用 IOptions 接口将其注入到需要的地方。以下是一个示例的 JwtSettings 类:

csharpCopy codepublic class JwtSettings {     public string Issuer { get; set; }     public string Audience { get; set; }     public string SecretKey { get; set; }     public int AccessTokenExpirationMinutes { get; set; }     public int RefreshTokenExpirationMinutes { get; set; } } 

这个类定义了与 appsettings.json 文件中的配置项相对应的属性。

用户模型

// Models/User.cs  public class UserModel {     public string Id { get; set; }     public string Username { get; set; }     public string Password { get; set; }     public string Email { get; set; }     public string RefreshToken {get; set; } }  

实现JWT登录认证

现在,我们可以开始实现JWT登录认证的逻辑。我们将创建一个JwtService类,用于生成和验证JWT令牌。在您的ASP.NET Core项目中,创建一个名为JwtService.cs的类文件,然后添加以下代码:

using System; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Tokens;  public interface IJwtService {     string GenerateAccessToken(UserModel user);     string GenerateRefreshToken();     bool ValidateAccessToken(string token); }  public class JwtService : IJwtService {     private readonly JwtConfig _jwtConfig;      public JwtService(IConfiguration configuration)     {         _jwtConfig = configuration.GetSection("JwtConfig").Get<JwtSettings>();     }      public string GenerateAccessToken(UserModel user)     {         // 设置Token的Claims         List<Claim> claims = new List<Claim>         {             new Claim(ClaimTypes.NameIdentifier, user.Id),             new Claim(ClaimTypes.Name, user.Username)         };          // 生成Token的密钥         SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(appSettings.SecretKey));          // 生成Token的签名证书         SigningCredentials creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256Signature);          // 设置Token的过期时间         DateTime expires = DateTime.Now.AddMinutes(appSettings.AccessTokenExpirationMinutes);          // 创建Token         JwtSecurityToken token = new JwtSecurityToken(             appSettings.Issuer,             appSettings.Audience,             claims,             expires: expires,             signingCredentials: creds         );          // 生成Token字符串         string tokenString = new JwtSecurityTokenHandler().WriteToken(token);          return tokenString;     }      public string GenerateRefreshToken()     {         var randomNumber = new byte[32];         using (var rng = new RNGCryptoServiceProvider())         {             rng.GetBytes(randomNumber);             return Convert.ToBase64String(randomNumber);         }     }      public bool ValidateAccessToken(string token)     {         var tokenHandler = new JwtSecurityTokenHandler();         var key = Encoding.UTF8.GetBytes(_jwtConfig.SecretKey);         try         {             tokenHandler.ValidateToken(token, new TokenValidationParameters             {                 ValidateIssuer = true,                 ValidateAudience = true,                 ValidateLifetime = true,                 ValidateIssuerSigningKey = true,                 ValidIssuer = _jwtConfig.Issuer,                 ValidAudience = _jwtConfig.Audience,                 IssuerSigningKey = new SymmetricSecurityKey(key)             }, out var validatedToken);         }         catch (Exception)         {             return false;         }         return true;     } } 

这里我们创建了一个JwtService类,实现了IJwtService接口。该服务类通过依赖注入方式注入了IConfiguration,从而可以在构造函数中读取JwtConfig配置。

  • JwtService类中包含了生成访问令牌和刷新令牌的方法,以及验证访问令牌和从访问令牌中获取用户主体的方法。其中,

    • GenerateAccessToken方法使用JwtSecurityTokenHandler来生成访问令牌,并设置了过期时间、签名等参数。

    • GenerateRefreshToken方法生成一个随机的32位Base64编码的字符串作为刷新令牌。

    • ValidateAccessToken方法验证访问令牌和从访问令牌中获取用户主体。它们使用JwtSecurityTokenHandler来验证令牌的签名、过期时间等信息,并返回验证结果或用户主体。

用户信息加密

在JWT中,用户信息是以Claims的形式进行传递的,但默认情况下,这些信息是以明文的形式存储在令牌中的。为了保护用户信息的安全性,我们可以选择对用户信息进行加密。下面是一个简单的示例,演示如何在生成访问令牌时对用户信息进行加密。

public string GenerateAccessToken(UserModel user) {     // 设置Token的Claims     List<Claim> claims = new List<Claim>     {         new Claim(ClaimTypes.NameIdentifier, user.Id),         new Claim(ClaimTypes.Name, user.Username)     };      // 生成Token的密钥     SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(appSettings.SecretKey));      // 生成Token的签名证书     SigningCredentials creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256Signature);      // 设置Token的过期时间     DateTime expires = DateTime.Now.AddMinutes(appSettings.AccessTokenExpirationMinutes);      // 创建Token     JwtSecurityToken token = new JwtSecurityToken(         appSettings.Issuer,         appSettings.Audience,         claims,         expires: expires,         signingCredentials: creds     );      // 生成Token字符串     string tokenString = new JwtSecurityTokenHandler().WriteToken(token);      return tokenString; } 

在上面的示例中,我们使用了SigningCredentials类来设置加密的参数,包括加密密钥、加密算法等。这样生成的访问令牌在传递用户信息时会进行加密,增加了用户信息的安全性。

用户登录验证

在用户登录时,我们需要对用户提供的用户名和密码进行验证,并生成访问令牌和刷新令牌。下面是一个简单的示例,演示如何在ASP.NET Core中实现用户登录验证,并生成JWT令牌。

[HttpPost("login")] public IActionResult Login(UserModel model) {     // 验证用户名和密码     var isValidUser = ValidateUser(model.Username, model.Password);      if (!isValidUser)     {         return BadRequest(new { message = "Invalid username or password" });     }      // 生成访问令牌     var accessToken = _jwtService.GenerateAccessToken(model);      // 生成刷新令牌     var refreshToken = _jwtService.GenerateRefreshToken();      // 返回访问令牌和刷新令牌给客户端     return Ok(new     {         access_token = accessToken,         refresh_token = refreshToken     }); } 

在上面的示例中,我们通过调用_jwtService.GenerateAccessToken_jwtService.GenerateRefreshToken方法来生成访问令牌和刷新令牌,并将刷新令牌保存到数据库或其他持久化存储中,以便后续使用。

刷新令牌

在用户登录后,访问令牌会在一定时间后过期,此时用户需要使用刷新令牌来获取新的访问令牌,而无需重新登录。下面是一个简单的示例,演示如何在ASP.NET Core中实现刷新令牌功能。

[HttpPost("refresh")] public IActionResult RefreshToken(UserModel model) {     // 验证刷新令牌是否有效     var isValidRefreshToken = ValidateAccessToken(model.RefreshToken);      if (!isValidRefreshToken)     {         return BadRequest(new { message = "Invalid refresh token" });     }      // 生成新的访问令牌     var accessToken = _jwtService.GenerateAccessToken(model);      // 返回新的访问令牌给客户端     return Ok(new     {         access_token = accessToken     }); } 

在上面的示例中,我们通过调用_jwtService.GenerateAccessToken方法来生成新的访问令牌,并将其返回给客户端。在生成新的访问令牌时,我们可以使用之前保存的用户信息,例如用户名等。

用户登录简单验证

在每次请求时,我们需要对访问令牌进行验证,以确保用户的身份和权限。下面是一个简单的示例,演示如何在ASP.NET Core中实现对访问令牌的简单验证。

[HttpGet("profile")] [Authorize] public IActionResult GetUserProfile() {     // 获取当前用户的用户名     var username = ....;      // 根据用户名从数据库或其他持久化存储中获取用户信息     var userModel = ......;      if (userModel == null)     {         return NotFound(new { message = "userModel not found" });     }      // 返回用户信息给客户端     return Ok(new     {         username = userModel.Username,         email = userModel.Email     }); } 

在上面的示例中,我们通过添加[Authorize]属性来标记需要验证访问令牌的API端点。当客户端发送请求时,ASP.NET Core会自动验证访问令牌的有效性,并将用户信息存储在UserModel对象中,以便我们在方法内部访问。

总结

本篇博文通过一个简单的案例,介绍了如何使用 C# .NET 实现 JWT 登录验证,并处理用户信息的加密、刷新 Token、各种验证规则等功能。