core cookie 验证
Web API Jwt
》》》》用户信息
- namespace WebAPI001.Coms
- {
- public class Account
- {
- public string UserName { get; set; }
- public string UserPassword { get; set; }
- public string UserRole { get; set; }
- }
- }
复制代码 》》》获取jwt类
- using Microsoft.AspNetCore.Mvc;
- using Microsoft.IdentityModel.Tokens;
- using System.IdentityModel.Tokens.Jwt;
- using System.Runtime.CompilerServices;
- using System.Security.Claims;
- using System.Text;
- namespace WebAPI001.Coms
- {
- public class JwtHelper
- {
-
-
- public static string GenerateJWT(Account user, IConfiguration _configuration)
- {
- byte[] keyBytes = Encoding.UTF8.GetBytes(_configuration?.GetValue<string>("TokenParameter:Secret"));
- var securityKey = new SymmetricSecurityKey(keyBytes);
- // 创建JWT的签名凭证
- var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
- // 设置JWT的Claims
- var claims = new[]
- {
- new Claim(ClaimTypes.Name, user.UserName),
- new Claim(ClaimTypes.Role, user.UserRole),
- // 添加其他需要的声明
- };
- // 创建JWT的Token
- var token = new JwtSecurityToken(
- issuer: _configuration.GetValue<string>("TokenParameter:Issuer"),
- audience: _configuration.GetValue<string>("TokenParameter:Audience"),
- claims: claims,
- expires: DateTime.Now.AddMinutes(_configuration.GetValue<int>("TokenParameter:AccessExpiration")),
- signingCredentials: signingCredentials
- );
- // 生成JWT字符串
- var jwtToken = new JwtSecurityTokenHandler().WriteToken(token);
- return jwtToken;
- }
- }
- }
复制代码

- // Core 自带官方 JWT认证
- // 开启Bearer 认证
- builder.Services.AddAuthentication(options =>
- {
- // 设置默认的身份验证和挑战方案为 JwtBearer
- options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
- options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
- }) // 配置 JWT Bearer 选项
- .AddJwtBearer(options =>
- {
- // 配置 Token 验证参数
- options.TokenValidationParameters = new TokenValidationParameters
- {
- // 验证发行者
- ValidateIssuer = true,
- // 验证受众
- ValidateAudience = true,
- // 验证令牌有效期
- ValidateLifetime = true,
- // 验证签名密钥
- ValidateIssuerSigningKey = true,
- // 发行者
- ValidIssuer = builder.Configuration["TokenParameter:Issuer"],
- // 受众
- ValidAudience = builder.Configuration["JokenParameter:Audience"],
- // 签名密钥
- IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["TokenParameter:Secret"])),
- AudienceValidator = (m, n, z) => {
- //自定义验证逻辑
- return true;
- }
- };
- options.Events = new JwtBearerEvents
- {
- OnAuthenticationFailed = context =>
- {
- // 如果过期,则把 是否过期 添加到 , 返回头信息中
- if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
- {
- context.Response.Headers.Add("Token-Expired", "true");
- }
- return Task.CompletedTask;
- }
- //OnForbidden
- //OnChallenge
- //OnMessageReceived
- //OnTokenValidated
- };
- });
复制代码



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