using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; namespace Utils.Jwt { /// /// Token生成类 /// public class TokenHelper : ITokenHelper { private readonly IOptions _options; private NLog.Logger logger; public TokenHelper(IOptions options) { _options = options; logger = NLog.Web.NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger(); } /// /// 根据一个对象通过反射提供负载生成token /// /// /// /// public TnToken CreateToken(T user) where T : class { //携带的负载部分,类似一个键值对 List claims = new List(); //这里我们用反射把model数据提供给它 foreach (var item in user.GetType().GetProperties()) { object obj = item.GetValue(user); string value = ""; if (obj != null) value = obj.ToString(); claims.Add(new Claim(item.Name, value)); } //创建token return CreateTokenString(claims); } /// /// 根据键值对提供负载生成token /// /// /// public TnToken CreateToken(Dictionary keyValuePairs) { //携带的负载部分,类似一个键值对 List claims = new List(); //这里我们通过键值对把数据提供给它 foreach (var item in keyValuePairs) { claims.Add(new Claim(item.Key, item.Value)); } //创建token return CreateTokenString(claims); } /// /// 生成token /// /// List的 Claim对象 /// private TnToken CreateTokenString(List claims) { var now = DateTime.Now; var expires = now.Add(TimeSpan.FromMinutes(_options.Value.AccessTokenExpiresMinutes)); var token = new JwtSecurityToken( issuer: _options.Value.Issuer,//Token发布者 audience: _options.Value.Audience,//Token接受者 claims: claims,//携带的负载 notBefore: now,//当前时间token生成时间 expires: expires,//过期时间 signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_options.Value.IssuerSigningKey)), SecurityAlgorithms.HmacSha256)); return new TnToken { TokenStr = new JwtSecurityTokenHandler().WriteToken(token), Expires = expires }; } /// /// 验证身份 验证签名的有效性 /// /// /// 自定义各类验证; 是否包含那种申明,或者申明的值, public bool ValiToken(string encodeJwt, Func, bool> validatePayLoad = null) { var success = true; var jwtArr = encodeJwt.Split('.'); if (jwtArr.Length < 3)//数据格式都不对直接pass { return false; } var header = JsonConvert.DeserializeObject>(Base64UrlEncoder.Decode(jwtArr[0])); //logger.Info($"header:{header}"); var payLoad = JsonConvert.DeserializeObject>(Base64UrlEncoder.Decode(jwtArr[1])); //logger.Info($"payLoad:{payLoad}"); //配置文件中取出来的签名秘钥 var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(_options.Value.IssuerSigningKey)); //验证签名是否正确(把用户传递的签名部分取出来和服务器生成的签名匹配即可) success = success && string.Equals(jwtArr[2], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArr[0], ".", jwtArr[1]))))); if (!success) { return success;//签名不正确直接返回 } //其次验证是否在有效期内(也应该必须) var now = ToUnixEpochDate(DateTime.UtcNow); success = success && (now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString())); //不需要自定义验证不传或者传递null即可 if (validatePayLoad == null) return true; //再其次 进行自定义的验证 success = success && validatePayLoad(payLoad); return success; } /// /// 时间转换 /// /// /// private long ToUnixEpochDate(DateTime date) { return (long)Math.Round((date.ToUniversalTime() - new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero)).TotalSeconds); } /// /// 校验token状态 /// /// /// /// /// public TokenType ValiTokenState(string encodeJwt, Func, bool> validatePayLoad, Action> action) { var jwtArr = encodeJwt.Split('.'); if (jwtArr.Length < 3)//数据格式都不对直接pass { return TokenType.Fail; } var header = JsonConvert.DeserializeObject>(Base64UrlEncoder.Decode(jwtArr[0])); //logger.Info($"header:{Base64UrlEncoder.Decode(jwtArr[0])}"); var payLoad = JsonConvert.DeserializeObject>(Base64UrlEncoder.Decode(jwtArr[1])); //logger.Info($"payLoad:{Base64UrlEncoder.Decode(jwtArr[1])}"); var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(_options.Value.IssuerSigningKey)); //logger.Info($"jwtArr[2]:{jwtArr[2]}"); var str = Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArr[0], ".", jwtArr[1])))); //logger.Info($"str:{str}"); //验证签名是否正确(把用户传递的签名部分取出来和服务器生成的签名匹配即可) if (!string.Equals(jwtArr[2], str)) { //logger.Info($"TokenType:{TokenType.Fail}"); return TokenType.Fail; } //其次验证是否在有效期内(必须验证) var now = ToUnixEpochDate(DateTime.UtcNow); if (!(now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString()))) { return TokenType.Expired; } //不需要自定义验证不传或者传递null即可 if (validatePayLoad == null) { action(payLoad); return TokenType.Ok; } //再其次 进行自定义的验证 if (!validatePayLoad(payLoad)) { //logger.Info($"validatePayLoad"); return TokenType.Fail; } //可能需要获取jwt摘要里边的数据,封装一下方便使用 action(payLoad); return TokenType.Ok; } } }