core Webapi jwt 认证 JWT提前撤回

诗林  金牌会员 | 2024-12-22 18:00:18 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 581|帖子 581|积分 1743

core cookie 验证
Web API Jwt






》》》》用户信息
  1. namespace WebAPI001.Coms
  2. {
  3.     public class Account
  4.     {
  5.         public string UserName { get; set; }
  6.         public string UserPassword { get; set; }
  7.         public string UserRole { get; set; }
  8.     }
  9. }
复制代码
》》》获取jwt类
  1. using Microsoft.AspNetCore.Mvc;
  2. using Microsoft.IdentityModel.Tokens;
  3. using System.IdentityModel.Tokens.Jwt;
  4. using System.Runtime.CompilerServices;
  5. using System.Security.Claims;
  6. using System.Text;
  7. namespace WebAPI001.Coms
  8. {
  9.     public  class JwtHelper
  10.     {      
  11.       
  12.       
  13.         public static string GenerateJWT(Account user, IConfiguration _configuration)
  14.         {           
  15.             byte[] keyBytes = Encoding.UTF8.GetBytes(_configuration?.GetValue<string>("TokenParameter:Secret"));
  16.             var securityKey = new SymmetricSecurityKey(keyBytes);
  17.             // 创建JWT的签名凭证
  18.             var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
  19.             // 设置JWT的Claims
  20.             var claims = new[]
  21.             {
  22.                new Claim(ClaimTypes.Name, user.UserName),
  23.                new Claim(ClaimTypes.Role, user.UserRole),
  24.                // 添加其他需要的声明
  25.             };
  26.             // 创建JWT的Token
  27.             var token = new JwtSecurityToken(
  28.                issuer: _configuration.GetValue<string>("TokenParameter:Issuer"),
  29.                audience: _configuration.GetValue<string>("TokenParameter:Audience"),
  30.                claims: claims,
  31.                expires: DateTime.Now.AddMinutes(_configuration.GetValue<int>("TokenParameter:AccessExpiration")),
  32.                signingCredentials: signingCredentials
  33.             );
  34.             // 生成JWT字符串
  35.             var jwtToken = new JwtSecurityTokenHandler().WriteToken(token);
  36.             return jwtToken;
  37.         }
  38.     }
  39. }
复制代码


  1. // Core 自带官方 JWT认证
  2. // 开启Bearer 认证
  3. builder.Services.AddAuthentication(options =>
  4. {
  5.      // 设置默认的身份验证和挑战方案为 JwtBearer
  6.      options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
  7.      options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
  8. }) // 配置 JWT Bearer 选项
  9. .AddJwtBearer(options =>
  10. {
  11.      // 配置 Token 验证参数
  12.      options.TokenValidationParameters = new TokenValidationParameters
  13.      {
  14.          // 验证发行者
  15.          ValidateIssuer = true,
  16.          // 验证受众
  17.          ValidateAudience = true,
  18.          // 验证令牌有效期
  19.          ValidateLifetime = true,
  20.          // 验证签名密钥
  21.          ValidateIssuerSigningKey = true,
  22.          // 发行者
  23.          ValidIssuer = builder.Configuration["TokenParameter:Issuer"],
  24.          // 受众
  25.          ValidAudience = builder.Configuration["JokenParameter:Audience"],
  26.          // 签名密钥
  27.          IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["TokenParameter:Secret"])),
  28.          AudienceValidator = (m, n, z) => {
  29.              //自定义验证逻辑
  30.              return true;
  31.          }
  32.      };
  33.      options.Events = new JwtBearerEvents
  34.      {
  35.          OnAuthenticationFailed = context =>
  36.          {
  37.              // 如果过期,则把 是否过期 添加到  , 返回头信息中
  38.              if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
  39.              {
  40.                  context.Response.Headers.Add("Token-Expired", "true");
  41.              }
  42.              return Task.CompletedTask;
  43.          }
  44.          //OnForbidden
  45.          //OnChallenge
  46.          //OnMessageReceived
  47.          //OnTokenValidated
  48.      };
  49. });
复制代码




源码
JWT提前撤回

JWT 具有不可打消性

JWT自身不具有可打消性子,要JWT失效,需要在服务端存储每个客户的JWT 对应的信息,进行比较,
例如假如 要把某个客户JWT移除服务端的存储, 服务端不存在JWT,则哀求是 不准许的。
获取在JWT中添加一个字段,代表哀求版本,在服务端【数据库存储最新版本】,哀求时进行 版本比较。
当遇到用户被删除、用户在另一个设备上登陆等场景需要将JWT提前撤回,但是JWT是保存在客户端,无法在服务器中进行删除。
解决思路是在用户表中增加一列JWTVersion,用来存储最后一次发放出去的令牌版本号,每次登陆、发放令牌的时间都让JWTVersion自增,当服务器收到客户端提交的JWT后,将客户端的JWTVersion和服务器的进行比较,假如客户端的值小于服务器中的值则过期。
》》》用户实体类
  1. public  class Account{
  2.         public string ID {get;set;}
  3.         public string UserName{get;set;}
  4.         public int JWTVersion{get;set;}
  5.         public string PassWord {get;set;}
  6. }
复制代码
  1. using Microsoft.AspNetCore.Mvc;
  2. using Microsoft.IdentityModel.Tokens;
  3. using System.IdentityModel.Tokens.Jwt;
  4. using System.Runtime.CompilerServices;
  5. using System.Security.Claims;
  6. using System.Text;
  7. namespace WebAPI001.Coms
  8. {
  9.     public  class JwtHelper
  10.     {      
  11.         public static string GenerateJWT(Account user, IConfiguration _configuration)
  12.         {           
  13.             byte[] keyBytes = Encoding.UTF8.GetBytes(_configuration?.GetValue<string>("TokenParameter:Secret"));
  14.             var securityKey = new SymmetricSecurityKey(keyBytes);
  15.             // 创建JWT的签名凭证
  16.             var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
  17.             // 设置JWT的Claims
  18.             var claims = new[]
  19.             {
  20.                new Claim(ClaimTypes.NameIdentifier, user.ID),
  21.                new Claim(ClaimTypes.Name, user.UserName),
  22.                new Claim(ClaimTypes.Version, user.JWTVersion),
  23.                // 添加其他需要的声明
  24.             };
  25.             // 创建JWT的Token
  26.             var token = new JwtSecurityToken(
  27.                issuer: _configuration.GetValue<string>("TokenParameter:Issuer"),
  28.                audience: _configuration.GetValue<string>("TokenParameter:Audience"),
  29.                claims: claims,
  30.                expires: DateTime.Now.AddMinutes(_configuration.GetValue<int>("TokenParameter:AccessExpiration")),
  31.                signingCredentials: signingCredentials
  32.             );
  33.             // 生成JWT字符串
  34.             var jwtToken = new JwtSecurityTokenHandler().WriteToken(token);
  35.             return jwtToken;
  36.         }
  37.     }
  38. }
复制代码
控制器
  1. [AllowAnonymous]
  2. [HttpPost]
  3. public IActionResult Login(Account ant)
  4. {
  5.         //用户需要和数据库验证  
  6.         if(ant.UserName=="zen" && ant.PassWord =="123")
  7.         {
  8.                 //  从数据库获取用户的信息
  9.                 Account ant=xxxx
  10.                 ant.JWTVersion+=1;
  11.                 var token = JwtHelper.GenerateJWT(ant);
  12.         }
  13.        
  14. }
复制代码
》》》全局注册 过滤器
  1. builder.Services.Configure<MvcOptions>(opt=>{
  2.     opt.Filters.Add<JWTValidationFilter>();
  3. })
复制代码
》》》筛选器
  1. public class JWTValidationFilter : IAsyncActionFilter
  2. {
  3.     private IMemoryCache memCache;
  4.     private UserManager<Account> userMgr;
  5.     public JWTValidationFilter(IMemoryCache memCache, UserManager<Account> userMgr)
  6.     {
  7.         this.memCache = memCache;
  8.         this.userMgr = userMgr;
  9.     }
  10.     public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
  11.     {
  12.         var claimUserId = context.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier);//查询用户信息
  13.         //对于登录接口等没有登录的,直接跳过
  14.         if (claimUserId == null)
  15.         {
  16.             await next();
  17.             return;
  18.         }
  19.         long userId = long.Parse(claimUserId!.Value);
  20.         //放到内存缓存中
  21.         string cacheKey = $"JWTValidationFilter.UserInfo.{userId}";
  22.         User user = await memCache.GetOrCreateAsync(cacheKey, async e => {
  23.             e.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(5);
  24.             return await userMgr.FindByIdAsync(userId.ToString());
  25.         });
  26.         if (user == null)//为查找到用户,可能已经别删除
  27.         {
  28.             var result = new ObjectResult($"UserId({userId}) not found");
  29.             result.StatusCode = (int)HttpStatusCode.Unauthorized;
  30.             context.Result = result;
  31.             return;
  32.         }
  33.         var claimVersion = context.HttpContext.User.FindFirst(ClaimTypes.Version);
  34.         //jwt中保存的版本号
  35.         long jwtVerOfReq = long.Parse(claimVersion!.Value);
  36.         //由于内存缓存等导致的并发问题,
  37.         //假如集群的A服务器中缓存保存的还是版本为5的数据,但客户端提交过来的可能已经是版本号为6的数据。因此只要是客户端提交的版本号>=服务器上取出来(可能是从Db,也可能是从缓存)的版本号,那么也是可以的
  38.         if (jwtVerOfReq >= user.JWTVersion)
  39.         {
  40.             await next();
  41.         }
  42.         else
  43.         {
  44.             var result = new ObjectResult($"JWTVersion mismatch");
  45.             result.StatusCode = (int)HttpStatusCode.Unauthorized;
  46.             context.Result = result;
  47.             return;
  48.         }
  49.     }
  50. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

诗林

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表