翼度科技»论坛 编程开发 .net 查看内容

.net 8 实现【JWT】 十分钟领略【无状态设计】token

15

主题

15

帖子

45

积分

新手上路

Rank: 1

积分
45
    本文主要分为两个部分:
    1、概念
    2、.net 8 demo
    第一部分主要描述所有与 JWT 相关的概念以及词汇,及其原理;第二部分是代码示例。
 
 
1、概念

JWT
json web token 是一种开放标准(RFC 7519),是指定一种数据格式(json) 和数据结构 (header, payload, signature) ,
用于网络应用间信息传输,一般是用于客户端与服务器之间的安全校验
authentication
鉴权;可以为用户创建 token,可以校验 token 有效性
authorization
批准 / 授权;是一种配置,约束访问条件,例如:
[Authorize(Roles = "Admin")] // 管理员才可访问
[Authorize(Policy = "EmployeeWithDepartment")] // 符合特定策略才可访问
bearer
持票人,使用场景在于请求时,header 的格式
例如请求头部的参数 "Authorization" : "Bearer tokenString"
 
一个 token 由 3 个部分组成,并且以实心句号 . 分割,以下为一个 token 示例:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoidXNlcjEyMyIsIm5iZiI6MTczMTM5MzYzNiwiZXhwIjoxNzMxMzk3MjM2LCJpYXQiOjE3MzEzOTM2MzYsImlzcyI6InlvdXJkb21haW4uY29tIiwiYXVkIjoieW91ci1hcGktYXVkaWVuY2UifQ.q2zUeZ3RAVIxg5CVNI2Pjq9Nfvue3_tCQagDRJmStYI
 
 
token part
decode (by base64)
remark
header
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
{ "alg" : "HS256", "typ" : "JWT" }
token metadata
说明用于加密的算法类型
payload
(有效载荷)
eyJuYW1lIjoidXNlcjEyMyIsIm5iZiI6MTczMTM5MzYzNiwiZXhwIjoxNzMxMzk3MjM2LCJpYXQiOjE3MzEzOTM2MzYsImlzcyI6InlvdXJkb21haW4uY29tIiwiYXVkIjoieW91ci1hcGktYXVkaWVuY2UifQ{
    "name": "user123",
    "nbf": 1731393636,
    "exp": 1731397236,
    "iat": 1731393636,
    "iss": "yourdomain.com",
    "aud": "your-api-audience"
}
自定义的用户信息(claims),
以及一些必要信息:
nbf : not before, token 生效时间
exp : expiration time, token 过期时间
iat : issued at 签发时间
iss : issuer of the token 签发者(比如服务端名字)
aud : audience,该 token 的受众,可以是服务名称 或者 api 名称等等
signature
q2zUeZ3RAVIxg5CVNI2Pjq9Nfvue3_tCQagDRJmStYI
N/A
signature:是根据 header 中指定的算法以及在服务端密钥,对 payload 加密得到的哈希值;
用于服务端校验 token 是否签发自服务端。
 
(请求时 token 的 payload 与解密 signature 后得到的payload 需要一致,所以擅改 payload 会导致token 无效)
 
无状态(stateless):指服务端不需要存储任何关于用户会话的信息。
signature 的设计保证了 token 的安全性,所以不再需要另外存储用户会话状态,以此减少服务端压力和复杂性,做到“无状态”化。
* base64 transfer online : Base64编码_base64在线解码_base64加密在线转换
 
 
 
2、.net 8 demo

2.1 Authentication

包引用
  1. using Microsoft.IdentityModel.Tokens;
  2. using System.IdentityModel.Tokens.Jwt;
  3. using System.Security.Claims;
复制代码
创建 token 的方法
  1. 1 public string GenerateJwtToken(string userName)
  2. 2 {
  3. 3
  4. 4     var tokenHander = new JwtSecurityTokenHandler();
  5. 5     var key = Encoding.ASCII.GetBytes("your secret key");
  6. 6     var tokenDescriptor = new SecurityTokenDescriptor
  7. 7     {
  8. 8         // configure the custome information in the Claim instance
  9. 9         Subject = new System.Security.Claims.ClaimsIdentity(new[] { new Claim("name", userName) }),
  10. 10
  11. 11         // configure the expiration time
  12. 12         Expires = DateTime.UtcNow.AddHours(1),
  13. 13
  14. 14         // issuer could be your service name or other, and it will be the [iss] of the token
  15. 15         Issuer = "xx Servicing",
  16. 16
  17. 17         // audience could be the audience of your service, and it will be the [aud] of the token
  18. 18         Audience = "your api audience",
  19. 19
  20. 20         // configure secret algorithm and secret key for signature part
  21. 21         SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
  22. 22
  23. 23     };
  24. 24
  25. 25     var token = tokenHander.CreateToken(tokenDescriptor);
  26. 26     return tokenHander.WriteToken(token);
  27. 27 }
复制代码
 
验证 token 的配置
  1. 1 builder.Services.AddAuthentication(options =>
  2. 2 {
  3. 3     options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
  4. 4     options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
  5. 5 })
  6. 6 .AddJwtBearer(options =>
  7. 7 {
  8. 8     options.TokenValidationParameters = new TokenValidationParameters
  9. 9     {
  10. 10         // validate the token expiration
  11. 11         ValidateLifetime = true,
  12. 12
  13. 13         // validate signature
  14. 14         ValidateIssuerSigningKey = true,
  15. 15
  16. 16         // need to sure the issuer/audience value here is same with the issuer/audience while token generating
  17. 17         ValidateIssuer = true,
  18. 18         ValidateAudience = true,
  19. 19         ValidIssuer = "yourdomain.com",
  20. 20         ValidAudience = "your-api-audience",
  21. 21
  22. 22         //ValidateIssuer = false, // Skip issuer validation
  23. 23         //ValidateAudience = false, // Skip audience validation
  24. 24
  25. 25         IssuerSigningKey = new SymmetricSecurityKey("your secret key"),
  26. 26         ClockSkew = TimeSpan.FromMinutes(5) // Allow for clock skew
  27. 27
  28. 28     };
  29. 29
  30. 30 });
复制代码
 
开启 authentication 和 authorization
  1. 1 app.UseAuthentication();
  2. 2 app.UseAuthorization();
复制代码
 
!注意:
1、此处的 Authentication,Authorization 都是 Miscroft.AspNetCore 自带的中间件。
2、Authentication 是鉴权,Authorization 是授权 / 批准,这两个中间件的位置不能错,必须是:鉴权在前。
Authentication 中间件主要负责验证 token ,比如检查签名是否正确,检查 token 有效期。
当 Authentication 中间件成功验证令牌后,它会将 token 中的用户信息(像是用户ID、角色等)写入到 httpContext,再传递给 Authorization 中间件。此时,Authorization 会拿到有效的 httpContext.User 属性,这是一个 ClaimsPrincipal 对象,包含了用户信息声明(claims);
若绕开了 Authentication 中间件,httpContext 直接到 Authorization ,此时会找不到合法的 httpContext.User,所以返回 401 Unauthorized 错误。
 
 
2.2 Authorization

2.2.1 Authorize
  1. 1     // 标注 Authorize 后访问该api时会验证token
  2. 2     [Authorize]
  3. 3     [HttpGet("GetList")]
  4. 4     public IEnumerable<WeatherForecast> GetList()
  5. 5     {
  6. 6         ..
  7. 7     }
复制代码
 
2.2.2 Policy 策略
  1. 1     // 在 Startup.cs 中配置策略
  2. 2     services.AddAuthorization(options =>
  3. 3     {  
  4. 4         options.AddPolicy("Over18", policy =>
  5. 5             policy.RequireClaim("Age", "18"));  // 验证 token 中的 age 参数是否等于 18
  6. 6     });
  7. 7
  8. 8     // 在 Controller 中使用策略
  9. 9     [Authorize(Policy = "Over18")]
  10. 10     public IActionResult RestrictedContent()
  11. 11     {
  12. 12        ..
  13. 13     }
复制代码
 
  1. 1     // 验证 token 中的 role 参数是否 ("Admin", "Manager")中的成员,注:大小写敏感
  2. 2     services.AddAuthorization(options =>
  3. 3     {
  4. 4         options.AddPolicy("AdminOrManager", policy =>
  5. 5             policy.RequireRole("Admin", "Manager"));
  6. 6     });
  7. 7
  8. 8     // 多个“并且”条件的声明
  9. 9     services.AddAuthorization(options =>
  10. 10     {
  11. 11
  12. 12         options.AddPolicy("EmployeeWithDepartment", policy =>
  13. 13             policy.RequireClaim("EmployeeNumber")
  14. 14                   .RequireClaim("Department", "HR", "IT"));
  15. 15     });
复制代码
 
2.2.3 AllowAnonymous

 [Authorize] 可以修饰在 controller 上,这意味着该 controller 下的所有 api 都需要验证token,此时可以通过修饰 [AllowAnonymous] 特别指定某一个 api 不需要 token 验证。
  1. 1 [Authorize]
  2. 2 public class AccountController : Controller
  3. 3 {
  4. 4
  5. 5     [AllowAnonymous]
  6. 6     public IActionResult Login()
  7. 7     {
  8. 8         ..
  9. 9     }
  10. 10
  11. 11
  12. 12     public IActionResult Logout()
  13. 13     {
  14. 14         ..
  15. 15     }
  16. 16
  17. 17 }
复制代码
 
 
 
博客园的编辑器也太难用了吧!!
2024-11-13
 

来源:https://www.cnblogs.com/carmen-019/p/18544948
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!
来自手机

举报 回复 使用道具