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;
}
}
}