123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- 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
- {
- /// <summary>
- /// Token生成类
- /// </summary>
- public class TokenHelper : ITokenHelper
- {
- private readonly IOptions<JWTConfig> _options;
- private NLog.Logger logger;
- public TokenHelper(IOptions<JWTConfig> options)
- {
- _options = options;
- logger = NLog.Web.NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
- }
- /// <summary>
- /// 根据一个对象通过反射提供负载生成token
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="user"></param>
- /// <returns></returns>
- public TnToken CreateToken<T>(T user) where T : class
- {
- //携带的负载部分,类似一个键值对
- List<Claim> claims = new List<Claim>();
- //这里我们用反射把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);
- }
- /// <summary>
- /// 根据键值对提供负载生成token
- /// </summary>
- /// <param name="keyValuePairs"></param>
- /// <returns></returns>
- public TnToken CreateToken(Dictionary<string, string> keyValuePairs)
- {
- //携带的负载部分,类似一个键值对
- List<Claim> claims = new List<Claim>();
- //这里我们通过键值对把数据提供给它
- foreach (var item in keyValuePairs)
- {
- claims.Add(new Claim(item.Key, item.Value));
- }
- //创建token
- return CreateTokenString(claims);
- }
- /// <summary>
- /// 生成token
- /// </summary>
- /// <param name="claims">List的 Claim对象</param>
- /// <returns></returns>
- private TnToken CreateTokenString(List<Claim> 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 };
- }
- /// <summary>
- /// 验证身份 验证签名的有效性
- /// </summary>
- /// <param name="encodeJwt"></param>
- /// <param name="validatePayLoad">自定义各类验证; 是否包含那种申明,或者申明的值, </param>
- public bool ValiToken(string encodeJwt, Func<Dictionary<string, string>, bool> validatePayLoad = null)
- {
- var success = true;
- var jwtArr = encodeJwt.Split('.');
- if (jwtArr.Length < 3)//数据格式都不对直接pass
- {
- return false;
- }
- var header = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[0]));
- //logger.Info($"header:{header}");
- var payLoad = JsonConvert.DeserializeObject<Dictionary<string, string>>(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;
- }
- /// <summary>
- /// 时间转换
- /// </summary>
- /// <param name="date"></param>
- /// <returns></returns>
- private long ToUnixEpochDate(DateTime date)
- {
- return (long)Math.Round((date.ToUniversalTime() - new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero)).TotalSeconds);
- }
- /// <summary>
- /// 校验token状态
- /// </summary>
- /// <param name="encodeJwt"></param>
- /// <param name="validatePayLoad"></param>
- /// <param name="action"></param>
- /// <returns></returns>
- public TokenType ValiTokenState(string encodeJwt, Func<Dictionary<string, string>, bool> validatePayLoad, Action<Dictionary<string, string>> action)
- {
- var jwtArr = encodeJwt.Split('.');
- if (jwtArr.Length < 3)//数据格式都不对直接pass
- {
- return TokenType.Fail;
- }
- var header = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[0]));
- //logger.Info($"header:{Base64UrlEncoder.Decode(jwtArr[0])}");
- var payLoad = JsonConvert.DeserializeObject<Dictionary<string, string>>(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;
- }
- }
- }
|