ToB企服应用市场:ToB评测及商务社交产业平台

标题: ASP.NET CORE WEBAPI 登录 JWT 鉴权 ,接口权限验证 [打印本页]

作者: 缠丝猫    时间: 2023-4-21 18:22
标题: ASP.NET CORE WEBAPI 登录 JWT 鉴权 ,接口权限验证
JWT的简单使用

介绍

当今Web开发中,API的使用越来越广泛,而API的安全性也变得越来越重要。其中,JWT(JSON Web Token)鉴权和授权是一种常见的解决方案。
本篇文章将会介绍JWT鉴权和授权的原理、实现方式以及注意事项。
什么是JWT?

JWT是一种基于JSON格式的开放标准(RFC7519),用于在网络上传递声明信息的一种简洁、自包含的安全方式。JWT通常被用来在各个系统之间传递身份认证信息和用户授权信息。
安装相关 NuGet 包

在开始使用 JWT 进行授权鉴权之前,需要先安装 Microsoft.AspNetCore.Authentication.JwtBearer NuGet 包。可以使用 Visual Studio 的 NuGet 管理器或者命令行工具进行安装。
  1. Install-Package Microsoft.AspNetCore.Authentication.JwtBearer
复制代码
JWT的组成部分

JWT由三个部分组成:Header(头部)、Payload(负载)和Signature(签名)。
头部(Header)

头部通常由两部分组成:令牌类型(即JWT)和指定该令牌所使用的签名算法。例如:
  1. {
  2.   "alg": "HS256",
  3.   "typ": "JWT"
  4. }
复制代码
负载(Payload)

负载通常包含了需要传递的声明信息,声明信息由键值对组成。例如:
  1. {
  2.   "sub": "1234567890",
  3.   "name": "John Doe",
  4.   "iat": 1516239022
  5. }
复制代码
其中,“sub”表示主题(subject),可以是用户ID或其他标识符;“name”表示用户名;“iat”表示令牌发行时间。
签名(Signature)

签名是对Header和Payload的内容进行数字签名后得到的一串字符串。签名用于验证JWT是否被篡改或伪造。例如:
  1. HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
复制代码
其中,“secret”为使用该令牌的服务器端保存的密钥。
JWT的优点

JWT鉴权

JWT鉴权是指通过JWT来验证用户身份和权限。在使用JWT鉴权时,客户端将用户凭证(例如用户名和密码)发送给服务器,在服务器验证用户凭证有效后,生成一个JWT并将其返回给客户端。客户端在以后的请求中携带这个JWT,服务器通过验证JWT的签名和有效期等信息来验证用户身份和授权信息。
JWT鉴权流程

JWT鉴权实现

配置appsettings.json

我们需要在appsettings.json文件中配置JWT的相关信息。在您的ASP.NET Core项目中,找到appsettings.json文件,并添加以下配置:
  1. "Authentication": {
  2.     "SecretKey": "yourIssuer",
  3.     "Issuer": "yourAudience",
  4.     "Audience": "yourSecretKey"
  5.   },
复制代码
添加 JWT 鉴权服务

在ASP.NET Core中,可以使用JwtBearer认证方案来验证JWT。首先,在Startup.cs文件中添加以下代码:
  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3.     // 添加JWT身份验证服务
  4.     services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  5.          .AddJwtBearer(options =>
  6.          {
  7.              var secretByte = Encoding.UTF8.GetBytes(Configuration["Authentication:SecretKey"]);  // 从appsettings.json读取Jwt配置
  8.              options.TokenValidationParameters = new TokenValidationParameters()
  9.              {
  10.                  ValidateIssuer = true,
  11.                  ValidIssuer = Configuration["Authentication:Issuer"],
  12.                  ValidateAudience = true,
  13.                  ValidAudience = Configuration["Authentication:Audience"],
  14.                  ValidateLifetime = true,
  15.             
  16.                  ValidateIssuerSigningKey = true,
  17.                  IssuerSigningKey = new SymmetricSecurityKey(secretByte)
  18.              };
  19.          });
  20.     // 其他服务配置
  21. }
  22. public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
  23. {
  24.     // 其他中间件配置...
  25.     app.UseAuthentication();
  26.     app.UseAuthorization();
  27. }
复制代码
上面代码中,我们向依赖注入容器中注册了一个身份验证方案,名称为 JwtBearerDefaults.AuthenticationScheme,表示使用 JWT 进行身份验证。然后,我们使用 AddJwtBearer 扩展方法,将 JWT 鉴权服务添加到应用程序中。
在 AddJwtBearer 方法中,我们需要配置 TokenValidationParameters 来验证 JWT。其中,ValidateIssuer、ValidIssuer、ValidateAudience、ValidAudience 和 ValidateLifetime 属性用于验证 JWT 中的发行人、接收方、有效期等信息。IssuerSigningKey 属性表示密钥,用于对 JWT 进行数字签名。最后,ValidateIssuerSigningKey 属性用于验证 JWT 的签名是否正确。
生成JWT

在ASP.NET Core中,可以使用JwtSecurityToken类来创建JWT。例如:
  1. var signingAlgorithm = SecurityAlgorithms.HmacSha256;
  2. var claims = new[]
  3. {
  4.     new Claim(JwtRegisteredClaimNames.Sub,"user_id"),
  5.     new Claim(ClaimTypes.Role,"Admin"),
  6.     new Claim("UserId","12"),
  7. };
  8. var secretByte = Encoding.UTF8.GetBytes(_configuration["Authentication:SecretKey"]);
  9. var signingKey = new SymmetricSecurityKey(secretByte);
  10. var signingCredentials = new SigningCredentials(signingKey, signingAlgorithm);
  11. var token = new JwtSecurityToken(
  12.     issuer: _configuration["Authentication:Issuer"],
  13.     audience: _configuration["Authentication:Audience"],
  14.     claims,
  15.     notBefore: DateTime.UtcNow,
  16.     expires: DateTime.UtcNow.AddDays(1),
  17.     signingCredentials
  18. );
  19. var tokenStr = new JwtSecurityTokenHandler().WriteToken(token);
复制代码
在这里,我们首先创建了一个声明(Claims)列表,其中包含用户ID、角色信息。然后,我们指定了JWT的过期时间和签名算法,并使用SymmetricSecurityKey类来指定密钥。最后,我们使用JwtSecurityTokenHandler类将token转换为字符串形式的jwt。
验证JWT
  1. public bool ValidateAccessToken(string token)
  2.     {
  3.         var tokenHandler = new JwtSecurityTokenHandler();
  4.         var key = Encoding.UTF8.GetBytes(_jwtConfig.SecretKey);
  5.         try
  6.         {
  7.             tokenHandler.ValidateToken(token, new TokenValidationParameters
  8.             {
  9.                 ValidateIssuer = true,
  10.                 ValidateAudience = true,
  11.                 ValidateLifetime = true,
  12.                 ValidateIssuerSigningKey = true,
  13.                 ValidIssuer = _jwtConfig.Issuer,
  14.                 ValidAudience = _jwtConfig.Audience,
  15.                 IssuerSigningKey = new SymmetricSecurityKey(key)
  16.             }, out var validatedToken);
  17.         }
  18.         catch (Exception)
  19.         {
  20.             return false;
  21.         }
  22.         return true;
  23.     }
  24. }
复制代码
上面代码中,我们使用 JwtSecurityTokenHandler 类来验证 JWT 的真实性和完整性。其中,我们使用 TokenValidationParameters 来配置验证参数,包括是否验证 JWT 发行人、接收方、有效期等信息,以及使用哪个密钥对其进行数字签名。如果验证通过,则返回 true,否则返回 false。
JWT授权

JWT授权是指根据JWT中包含的声明信息来验证用户是否具有访问特定资源的权限。在使用JWT授权时,我们在JWT中添加了一些声明信息,例如用户所属角色、权限等,服务器可以通过这些信息来验证用户是否有权访问特定资源。
JWT授权流程

JWT授权实现

用户登录

创建传入DTO:LoginDto 、返回DTO:LoginOutDto类
  1. public class LoginDto
  2. {
  3.     public string UserName { get; set; }
  4.     public string Pwd { get; set; }
  5. }
  6. public class LoginOutDto
  7. {
  8.     public int Code { get; set; }
  9.     public string Msg { get; set; }
  10.     public string access_token { get; set; }
  11.     public string refresh_token { get; set; }
  12. }
复制代码
在用户登录时,我们需要对用户提供的用户名和密码进行验证,并生成访问令牌和刷新令牌。下面是一个简单的示例,演示如何在ASP.NET Core中实现用户登录验证,并生成JWT令牌。
  1. [HttpPost("login")]
  2. public LoginOutDto Login([FromBody] LoginDto input)
  3. {
  4.     var dto = new LoginOutDto();
  5.     try
  6.     {
  7.         if (input.UserName != "admin" || input.Pwd != "bb123456")
  8.         {
  9.             dto.Code = 500;
  10.             dto.Msg = "用户名或密码不正确";
  11.             dto.access_token = string.Empty;
  12.             return dto;
  13.         }
  14.         // 生成访问令牌
  15.         var accessToken = _jwtService.GenerateAccessToken();
  16.         // 生成刷新令牌
  17.         var refreshToken = _jwtService.GenerateRefreshToken();
  18.         dto.Code = 200;
  19.         dto.Msg = "登录成功";
  20.         dto.access_token = accessToken;
  21.         dto.refresh_token = refreshToken;
  22.         
  23.     }
  24.     catch (Exception ex)
  25.     {
  26.         dto.Code = 500;
  27.         dto.Msg = "登录失败:" + ex.Message;
  28.     }
  29.     return dto;
  30. }
复制代码
在上面的示例中,我们通过调用_jwtService.GenerateAccessToken和_jwtService.GenerateRefreshToken方法来生成访问令牌和刷新令牌,并将刷新令牌保存到数据库或其他持久化存储中,以便后续使用。
刷新令牌

在用户登录后,访问令牌会在一定时间后过期,此时用户需要使用刷新令牌来获取新的访问令牌,而无需重新登录。下面是一个简单的示例,演示如何在ASP.NET Core中实现刷新令牌功能。
  1. [HttpPost("refresh")]
  2. public IActionResult RefreshToken(UserModel model)
  3. {
  4.     // 验证刷新令牌是否有效
  5.     var isValidRefreshToken = ValidateAccessToken(model.RefreshToken);
  6.     if (!isValidRefreshToken)
  7.     {
  8.         return BadRequest(new { message = "Invalid refresh token" });
  9.     }
  10.     // 生成新的访问令牌
  11.     var accessToken = _jwtService.GenerateAccessToken(model);
  12.     // 返回新的访问令牌给客户端
  13.     return Ok(new
  14.     {
  15.         access_token = accessToken
  16.     });
  17. }
复制代码
在上面的示例中,我们通过调用_jwtService.GenerateAccessToken方法来生成新的访问令牌,并将其返回给客户端。在生成新的访问令牌时,我们可以使用之前保存的用户信息,例如用户名等
完整代码

下面是一个包含生成 JWT,解析 JWT,鉴权,授权和策略的完整示例。请注意,此示例仅供参考,请根据实际需求进行修改。
Startup.cs
  1. public class Startup
  2. {
  3.     public Startup(IConfiguration configuration)
  4.     {
  5.         Configuration = configuration;
  6.     }
  7.     public IConfiguration Configuration { get; }
  8.     // This method gets called by the runtime. Use this method to add services to the container.
  9.     public void ConfigureServices(IServiceCollection services)
  10.     {
  11.         services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  12.              .AddJwtBearer(options =>
  13.              {
  14.                  var secretByte = Encoding.UTF8.GetBytes(Configuration["Authentication:SecretKey"]);
  15.                  options.TokenValidationParameters = new TokenValidationParameters()
  16.                  {
  17.                      ValidateIssuer = true,
  18.                      ValidIssuer = Configuration["Authentication:Issuer"],
  19.                      ValidateAudience = true,
  20.                      ValidAudience = Configuration["Authentication:Audience"],
  21.                      ValidateLifetime = true,
  22.                      ValidateIssuerSigningKey = true,
  23.                      IssuerSigningKey = new SymmetricSecurityKey(secretByte)
  24.                  };
  25.              });
  26.         
  27.         // 注入IJwtService服务
  28.         services.AddSingleton<IJwtService, JwtService>();
  29.         services.AddControllers();
  30.         services.AddSwaggerGen(c =>
  31.         {
  32.             c.SwaggerDoc("v1", new OpenApiInfo { Title = "JWT.Demo", Version = "v1" });
  33.         });
  34.     }
  35.     // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
  36.     public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
  37.     {
  38.         if (env.IsDevelopment())
  39.         {
  40.             app.UseDeveloperExceptionPage();
  41.             app.UseSwagger();
  42.             app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "JWT.Demo v1"));
  43.         }
  44.         app.UseHttpsRedirection();
  45.         app.UseRouting();
  46.         // 身份验证
  47.         app.UseAuthentication();
  48.         // 授权
  49.         app.UseAuthorization();
  50.         app.UseEndpoints(endpoints =>
  51.         {
  52.             endpoints.MapControllers();
  53.         });
  54.     }
  55. }
复制代码
IJwtService&JwtService
  1. public interface IJwtService
  2. {
  3.     /// <summary>
  4.     /// 生成JWT
  5.     /// </summary>
  6.     /// <returns></returns>
  7.     string GenerateAccessToken();
  8.     /// <summary>
  9.     /// 刷新Token
  10.     /// </summary>
  11.     /// <returns></returns>
  12.     string GenerateRefreshToken();
  13.     /// <summary>
  14.     /// 验证JWT
  15.     /// </summary>
  16.     /// <param name="token"></param>
  17.     /// <returns></returns>
  18.     bool ValidateAccessToken(string token);
  19. }
  20. public class JwtService : IJwtService
  21. {
  22.     private readonly IConfiguration _configuration;
  23.     public JwtService(IConfiguration configuration)
  24.     {
  25.         _configuration = configuration;
  26.     }
  27.     /// <summary>
  28.     /// 生成JWT
  29.     /// </summary>
  30.     /// <returns></returns>
  31.     public string GenerateAccessToken()
  32.     {
  33.         var signingAlgorithm = SecurityAlgorithms.HmacSha256;
  34.         var claims = new[]
  35.         {
  36.             new Claim(JwtRegisteredClaimNames.Sub,"user_id"),
  37.             new Claim(ClaimTypes.Role,"Admin"),
  38.             new Claim("UserId","12"),
  39.         };
  40.         var secretByte = Encoding.UTF8.GetBytes(_configuration["Authentication:SecretKey"]);
  41.         var signingKey = new SymmetricSecurityKey(secretByte);
  42.         var signingCredentials = new SigningCredentials(signingKey, signingAlgorithm);
  43.         var token = new JwtSecurityToken(
  44.             issuer: _configuration["Authentication:Issuer"],
  45.             audience: _configuration["Authentication:Audience"],
  46.             claims,
  47.             notBefore: DateTime.UtcNow,
  48.             expires: DateTime.UtcNow.AddDays(1),
  49.             signingCredentials
  50.         );
  51.         var tokenStr = new JwtSecurityTokenHandler().WriteToken(token);
  52.         return tokenStr;
  53.     }
  54.     /// <summary>
  55.     /// 刷新Token
  56.     /// </summary>
  57.     /// <returns></returns>
  58.     public string GenerateRefreshToken()
  59.     {
  60.         var randomNumber = new byte[32];
  61.         using (var rng = new RNGCryptoServiceProvider())
  62.         {
  63.             rng.GetBytes(randomNumber);
  64.             return Convert.ToBase64String(randomNumber);
  65.         }
  66.     }
  67.     /// <summary>
  68.     /// 验证JWT
  69.     /// </summary>
  70.     /// <param name="token"></param>
  71.     /// <returns></returns>
  72.     public bool ValidateAccessToken(string token)
  73.     {
  74.         var tokenHandler = new JwtSecurityTokenHandler();
  75.         var key = Encoding.UTF8.GetBytes(_configuration["Authentication:SecretKey"]);
  76.         try
  77.         {
  78.             tokenHandler.ValidateToken(token, new TokenValidationParameters
  79.             {
  80.                 ValidateIssuer = true,
  81.                 ValidateAudience = true,
  82.                 ValidateLifetime = true,
  83.                 ValidateIssuerSigningKey = true,
  84.                 ValidIssuer = _configuration["Authentication:Issuer"],
  85.                 ValidAudience = _configuration["Authentication:Audience"],
  86.                 IssuerSigningKey = new SymmetricSecurityKey(key)
  87.             }, out var validatedToken);
  88.         }
  89.         catch (Exception)
  90.         {
  91.             return false;
  92.         }
  93.         return true;
  94.     }
  95. }
复制代码
AuthenticateController.cs
  1. [ApiController]
  2. [Route("auth")]
  3. public class AuthenticateController : ControllerBase
  4. {
  5.     private readonly IConfiguration _configuration;
  6.     private readonly IJwtService _jwtService;
  7.     public AuthenticateController(IConfiguration configuration, IJwtService jwtService)
  8.     {
  9.         _configuration = configuration;
  10.         _jwtService = jwtService;
  11.     }
  12.     [HttpPost("login")]
  13.     public LoginOutDto Login([FromBody] LoginDto input)
  14.     {
  15.         var dto = new LoginOutDto();
  16.         try
  17.         {
  18.             if (input.UserName != "admin" || input.Pwd != "bb123456")
  19.             {
  20.                 dto.Code = 500;
  21.                 dto.Msg = "用户名或密码不正确";
  22.                 dto.access_token = string.Empty;
  23.                 return dto;
  24.             }
  25.             // 生成访问令牌
  26.             var accessToken = _jwtService.GenerateAccessToken();
  27.             // 生成刷新令牌
  28.             var refreshToken = _jwtService.GenerateRefreshToken();
  29.             dto.Code = 200;
  30.             dto.Msg = "登录成功";
  31.             dto.access_token = accessToken;
  32.             dto.refresh_token = refreshToken;
  33.             
  34.         }
  35.         catch (Exception ex)
  36.         {
  37.             dto.Code = 500;
  38.             dto.Msg = "登录失败:" + ex.Message;
  39.         }
  40.         return dto;
  41.     }
  42.     [HttpPost("refresh")]
  43.     public IActionResult RefreshToken(string token)
  44.     {
  45.         // 验证刷新令牌是否有效
  46.         var isValidRefreshToken = _jwtService.ValidateAccessToken(token);
  47.         if (!isValidRefreshToken)
  48.         {
  49.             return BadRequest(new { message = "Invalid refresh token" });
  50.         }
  51.         // 生成新的访问令牌
  52.         var accessToken = _jwtService.GenerateAccessToken();
  53.         // 返回新的访问令牌给客户端
  54.         return Ok(new
  55.         {
  56.             access_token = accessToken
  57.         });
  58.     }
  59. }
复制代码
注意事项

结论

在.NET 5 中使用 JWT 进行授权鉴权是一种安全、可靠的身份验证方式。通过添加 JWT 鉴权服务、使用 Authorize 属性启用 JWT 授权、生成和验证 JWT、使用 UseAuthentication 和 UseAuthorization 中间件来启用身份验证和授权,并为不同的 API 设置不同的授权策略,可以轻松地实现 JWT 的授权鉴权功能。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4