|
JWT的简单使用
介绍
当今Web开发中,API的使用越来越广泛,而API的安全性也变得越来越重要。其中,JWT(JSON Web Token)鉴权和授权是一种常见的解决方案。
本篇文章将会介绍JWT鉴权和授权的原理、实现方式以及注意事项。
什么是JWT?
JWT是一种基于JSON格式的开放标准(RFC7519),用于在网络上传递声明信息的一种简洁、自包含的安全方式。JWT通常被用来在各个系统之间传递身份认证信息和用户授权信息。
安装相关 NuGet 包
在开始使用 JWT 进行授权鉴权之前,需要先安装 Microsoft.AspNetCore.Authentication.JwtBearer NuGet 包。可以使用 Visual Studio 的 NuGet 管理器或者命令行工具进行安装。- Install-Package Microsoft.AspNetCore.Authentication.JwtBearer
复制代码 JWT的组成部分
JWT由三个部分组成:Header(头部)、Payload(负载)和Signature(签名)。
头部(Header)
头部通常由两部分组成:令牌类型(即JWT)和指定该令牌所使用的签名算法。例如:- {
- "alg": "HS256",
- "typ": "JWT"
- }
复制代码 负载(Payload)
负载通常包含了需要传递的声明信息,声明信息由键值对组成。例如:- {
- "sub": "1234567890",
- "name": "John Doe",
- "iat": 1516239022
- }
复制代码 其中,“sub”表示主题(subject),可以是用户ID或其他标识符;“name”表示用户名;“iat”表示令牌发行时间。
签名(Signature)
签名是对Header和Payload的内容进行数字签名后得到的一串字符串。签名用于验证JWT是否被篡改或伪造。例如:- HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
复制代码 其中,“secret”为使用该令牌的服务器端保存的密钥。
JWT的优点
- 简洁:由于JWT采用了JSON格式,而JSON是一种轻量级的数据格式,因此JWT非常适合在多个服务之间传递信息。
- 自包含:JWT包含了所有必要的信息,因此不需要像Session那样在服务器端存储用户状态。
- 安全:JWT使用数字签名来保证消息完整性和真实性,并可以对负载进行加密处理。
JWT鉴权
JWT鉴权是指通过JWT来验证用户身份和权限。在使用JWT鉴权时,客户端将用户凭证(例如用户名和密码)发送给服务器,在服务器验证用户凭证有效后,生成一个JWT并将其返回给客户端。客户端在以后的请求中携带这个JWT,服务器通过验证JWT的签名和有效期等信息来验证用户身份和授权信息。
JWT鉴权流程
- 用户登录,向服务器提交身份凭证(例如用户名、密码)。
- 服务器验证身份凭证的有效性。
- 如果身份凭证有效,服务器生成一个JWT并将其返回给客户端。
- 客户端在以后的请求中携带JWT。
- 服务器从JWT中解析出用户ID等信息,并根据信息来验证用户身份和授权信息。
JWT鉴权实现
配置appsettings.json
我们需要在appsettings.json文件中配置JWT的相关信息。在您的ASP.NET Core项目中,找到appsettings.json文件,并添加以下配置:- "Authentication": {
- "SecretKey": "yourIssuer",
- "Issuer": "yourAudience",
- "Audience": "yourSecretKey"
- },
复制代码 添加 JWT 鉴权服务
在ASP.NET Core中,可以使用JwtBearer认证方案来验证JWT。首先,在Startup.cs文件中添加以下代码:- public void ConfigureServices(IServiceCollection services)
- {
- // 添加JWT身份验证服务
- services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
- .AddJwtBearer(options =>
- {
- var secretByte = Encoding.UTF8.GetBytes(Configuration["Authentication:SecretKey"]); // 从appsettings.json读取Jwt配置
- options.TokenValidationParameters = new TokenValidationParameters()
- {
- ValidateIssuer = true,
- ValidIssuer = Configuration["Authentication:Issuer"],
- ValidateAudience = true,
- ValidAudience = Configuration["Authentication:Audience"],
- ValidateLifetime = true,
-
- ValidateIssuerSigningKey = true,
- IssuerSigningKey = new SymmetricSecurityKey(secretByte)
- };
- });
- // 其他服务配置
- }
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
- {
- // 其他中间件配置...
- app.UseAuthentication();
- app.UseAuthorization();
- }
复制代码 上面代码中,我们向依赖注入容器中注册了一个身份验证方案,名称为 JwtBearerDefaults.AuthenticationScheme,表示使用 JWT 进行身份验证。然后,我们使用 AddJwtBearer 扩展方法,将 JWT 鉴权服务添加到应用程序中。
在 AddJwtBearer 方法中,我们需要配置 TokenValidationParameters 来验证 JWT。其中,ValidateIssuer、ValidIssuer、ValidateAudience、ValidAudience 和 ValidateLifetime 属性用于验证 JWT 中的发行人、接收方、有效期等信息。IssuerSigningKey 属性表示密钥,用于对 JWT 进行数字签名。最后,ValidateIssuerSigningKey 属性用于验证 JWT 的签名是否正确。
生成JWT
在ASP.NET Core中,可以使用JwtSecurityToken类来创建JWT。例如:- var signingAlgorithm = SecurityAlgorithms.HmacSha256;
- var claims = new[]
- {
- new Claim(JwtRegisteredClaimNames.Sub,"user_id"),
- new Claim(ClaimTypes.Role,"Admin"),
- new Claim("UserId","12"),
- };
- var secretByte = Encoding.UTF8.GetBytes(_configuration["Authentication:SecretKey"]);
- var signingKey = new SymmetricSecurityKey(secretByte);
- var signingCredentials = new SigningCredentials(signingKey, signingAlgorithm);
- var token = new JwtSecurityToken(
- issuer: _configuration["Authentication:Issuer"],
- audience: _configuration["Authentication:Audience"],
- claims,
- notBefore: DateTime.UtcNow,
- expires: DateTime.UtcNow.AddDays(1),
- signingCredentials
- );
- var tokenStr = new JwtSecurityTokenHandler().WriteToken(token);
复制代码 在这里,我们首先创建了一个声明(Claims)列表,其中包含用户ID、角色信息。然后,我们指定了JWT的过期时间和签名算法,并使用SymmetricSecurityKey类来指定密钥。最后,我们使用JwtSecurityTokenHandler类将token转换为字符串形式的jwt。
验证JWT
- 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;
- }
- }
复制代码 上面代码中,我们使用 JwtSecurityTokenHandler 类来验证 JWT 的真实性和完整性。其中,我们使用 TokenValidationParameters 来配置验证参数,包括是否验证 JWT 发行人、接收方、有效期等信息,以及使用哪个密钥对其进行数字签名。如果验证通过,则返回 true,否则返回 false。
JWT授权
JWT授权是指根据JWT中包含的声明信息来验证用户是否具有访问特定资源的权限。在使用JWT授权时,我们在JWT中添加了一些声明信息,例如用户所属角色、权限等,服务器可以通过这些信息来验证用户是否有权访问特定资源。
JWT授权流程
- 用户登录,向服务器提交身份凭证(例如用户名、密码)。
- 服务器验证身份凭证的有效性。
- 如果身份凭证有效,服务器生成一个JWT并将其返回给客户端。
- 客户端在以后的请求中携带JWT。
- 服务器从JWT中解析出用户信息和声明信息,并根据信息来验证用户是否有权访问特定资源。
JWT授权实现
用户登录
创建传入DTO:LoginDto 、返回DTO:LoginOutDto类- public class LoginDto
- {
- public string UserName { get; set; }
- public string Pwd { get; set; }
- }
- public class LoginOutDto
- {
- public int Code { get; set; }
- public string Msg { get; set; }
- public string access_token { get; set; }
- public string refresh_token { get; set; }
- }
复制代码 在用户登录时,我们需要对用户提供的用户名和密码进行验证,并生成访问令牌和刷新令牌。下面是一个简单的示例,演示如何在ASP.NET Core中实现用户登录验证,并生成JWT令牌。- [HttpPost("login")]
- public LoginOutDto Login([FromBody] LoginDto input)
- {
- var dto = new LoginOutDto();
- try
- {
- if (input.UserName != "admin" || input.Pwd != "bb123456")
- {
- dto.Code = 500;
- dto.Msg = "用户名或密码不正确";
- dto.access_token = string.Empty;
- return dto;
- }
- // 生成访问令牌
- var accessToken = _jwtService.GenerateAccessToken();
- // 生成刷新令牌
- var refreshToken = _jwtService.GenerateRefreshToken();
- dto.Code = 200;
- dto.Msg = "登录成功";
- dto.access_token = accessToken;
- dto.refresh_token = refreshToken;
-
- }
- catch (Exception ex)
- {
- dto.Code = 500;
- dto.Msg = "登录失败:" + ex.Message;
- }
- return dto;
- }
复制代码 在上面的示例中,我们通过调用_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方法来生成新的访问令牌,并将其返回给客户端。在生成新的访问令牌时,我们可以使用之前保存的用户信息,例如用户名等
完整代码
下面是一个包含生成 JWT,解析 JWT,鉴权,授权和策略的完整示例。请注意,此示例仅供参考,请根据实际需求进行修改。
Startup.cs
- public class Startup
- {
- public Startup(IConfiguration configuration)
- {
- Configuration = configuration;
- }
- public IConfiguration Configuration { get; }
- // This method gets called by the runtime. Use this method to add services to the container.
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
- .AddJwtBearer(options =>
- {
- var secretByte = Encoding.UTF8.GetBytes(Configuration["Authentication:SecretKey"]);
- options.TokenValidationParameters = new TokenValidationParameters()
- {
- ValidateIssuer = true,
- ValidIssuer = Configuration["Authentication:Issuer"],
- ValidateAudience = true,
- ValidAudience = Configuration["Authentication:Audience"],
- ValidateLifetime = true,
- ValidateIssuerSigningKey = true,
- IssuerSigningKey = new SymmetricSecurityKey(secretByte)
- };
- });
-
- // 注入IJwtService服务
- services.AddSingleton<IJwtService, JwtService>();
- services.AddControllers();
- services.AddSwaggerGen(c =>
- {
- c.SwaggerDoc("v1", new OpenApiInfo { Title = "JWT.Demo", Version = "v1" });
- });
- }
- // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
- {
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- app.UseSwagger();
- app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "JWT.Demo v1"));
- }
- app.UseHttpsRedirection();
- app.UseRouting();
- // 身份验证
- app.UseAuthentication();
- // 授权
- app.UseAuthorization();
- app.UseEndpoints(endpoints =>
- {
- endpoints.MapControllers();
- });
- }
- }
复制代码 IJwtService&JwtService
- public interface IJwtService
- {
- /// <summary>
- /// 生成JWT
- /// </summary>
- /// <returns></returns>
- string GenerateAccessToken();
- /// <summary>
- /// 刷新Token
- /// </summary>
- /// <returns></returns>
- string GenerateRefreshToken();
- /// <summary>
- /// 验证JWT
- /// </summary>
- /// <param name="token"></param>
- /// <returns></returns>
- bool ValidateAccessToken(string token);
- }
- public class JwtService : IJwtService
- {
- private readonly IConfiguration _configuration;
- public JwtService(IConfiguration configuration)
- {
- _configuration = configuration;
- }
- /// <summary>
- /// 生成JWT
- /// </summary>
- /// <returns></returns>
- public string GenerateAccessToken()
- {
- var signingAlgorithm = SecurityAlgorithms.HmacSha256;
- var claims = new[]
- {
- new Claim(JwtRegisteredClaimNames.Sub,"user_id"),
- new Claim(ClaimTypes.Role,"Admin"),
- new Claim("UserId","12"),
- };
- var secretByte = Encoding.UTF8.GetBytes(_configuration["Authentication:SecretKey"]);
- var signingKey = new SymmetricSecurityKey(secretByte);
- var signingCredentials = new SigningCredentials(signingKey, signingAlgorithm);
- var token = new JwtSecurityToken(
- issuer: _configuration["Authentication:Issuer"],
- audience: _configuration["Authentication:Audience"],
- claims,
- notBefore: DateTime.UtcNow,
- expires: DateTime.UtcNow.AddDays(1),
- signingCredentials
- );
- var tokenStr = new JwtSecurityTokenHandler().WriteToken(token);
- return tokenStr;
- }
- /// <summary>
- /// 刷新Token
- /// </summary>
- /// <returns></returns>
- public string GenerateRefreshToken()
- {
- var randomNumber = new byte[32];
- using (var rng = new RNGCryptoServiceProvider())
- {
- rng.GetBytes(randomNumber);
- return Convert.ToBase64String(randomNumber);
- }
- }
- /// <summary>
- /// 验证JWT
- /// </summary>
- /// <param name="token"></param>
- /// <returns></returns>
- public bool ValidateAccessToken(string token)
- {
- var tokenHandler = new JwtSecurityTokenHandler();
- var key = Encoding.UTF8.GetBytes(_configuration["Authentication:SecretKey"]);
- try
- {
- tokenHandler.ValidateToken(token, new TokenValidationParameters
- {
- ValidateIssuer = true,
- ValidateAudience = true,
- ValidateLifetime = true,
- ValidateIssuerSigningKey = true,
- ValidIssuer = _configuration["Authentication:Issuer"],
- ValidAudience = _configuration["Authentication:Audience"],
- IssuerSigningKey = new SymmetricSecurityKey(key)
- }, out var validatedToken);
- }
- catch (Exception)
- {
- return false;
- }
- return true;
- }
- }
复制代码 AuthenticateController.cs
- [ApiController]
- [Route("auth")]
- public class AuthenticateController : ControllerBase
- {
- private readonly IConfiguration _configuration;
- private readonly IJwtService _jwtService;
- public AuthenticateController(IConfiguration configuration, IJwtService jwtService)
- {
- _configuration = configuration;
- _jwtService = jwtService;
- }
- [HttpPost("login")]
- public LoginOutDto Login([FromBody] LoginDto input)
- {
- var dto = new LoginOutDto();
- try
- {
- if (input.UserName != "admin" || input.Pwd != "bb123456")
- {
- dto.Code = 500;
- dto.Msg = "用户名或密码不正确";
- dto.access_token = string.Empty;
- return dto;
- }
- // 生成访问令牌
- var accessToken = _jwtService.GenerateAccessToken();
- // 生成刷新令牌
- var refreshToken = _jwtService.GenerateRefreshToken();
- dto.Code = 200;
- dto.Msg = "登录成功";
- dto.access_token = accessToken;
- dto.refresh_token = refreshToken;
-
- }
- catch (Exception ex)
- {
- dto.Code = 500;
- dto.Msg = "登录失败:" + ex.Message;
- }
- return dto;
- }
- [HttpPost("refresh")]
- public IActionResult RefreshToken(string token)
- {
- // 验证刷新令牌是否有效
- var isValidRefreshToken = _jwtService.ValidateAccessToken(token);
- if (!isValidRefreshToken)
- {
- return BadRequest(new { message = "Invalid refresh token" });
- }
- // 生成新的访问令牌
- var accessToken = _jwtService.GenerateAccessToken();
- // 返回新的访问令牌给客户端
- return Ok(new
- {
- access_token = accessToken
- });
- }
- }
复制代码 注意事项
- 密钥管理:在使用JWT时,密钥是非常重要的,泄露密钥会导致安全问题。因此,密钥的生成、存储和更新都必须谨慎处理。
- 过期时间:在生成JWT时,要指定合适的过期时间,避免JWT过期后仍然可以使用。
- 签名算法:签名算法的选择很重要,不同的签名算法具有不同的安全性和效率。建议采用HMAC+
- SHA256或RSA算法。
- 不要存储敏感信息:JWT虽然安全,但仍然存在被盗用的可能性。因此,在生成JWT时,应避免将敏感信息(例如密码、信用卡号等)存储在负载中。
- 使用HTTPS:在使用JWT时,建议采用HTTPS协议来保证通讯的安全性。
- 谨慎处理“记住我”功能:在实现“记住我”功能时,需要谨慎处理,避免密钥泄露或用户凭证被盗用。
结论
在.NET 5 中使用 JWT 进行授权鉴权是一种安全、可靠的身份验证方式。通过添加 JWT 鉴权服务、使用 Authorize 属性启用 JWT 授权、生成和验证 JWT、使用 UseAuthentication 和 UseAuthorization 中间件来启用身份验证和授权,并为不同的 API 设置不同的授权策略,可以轻松地实现 JWT 的授权鉴权功能。
来源:https://www.cnblogs.com/l-monstar/archive/2023/04/21/17337768.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作! |
|