using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
namespace Wms_09.Tools
{
///
/// 配置token生成信息
///
public class JWTConfig
{
///
/// Token发布者
///
public string Issuer { get; set; }
///
/// oken接受者
///
public string Audience { get; set; }
///
/// 秘钥
///
public string IssuerSigningKey { get; set; }
///
/// 过期时间
///
public int AccessTokenExpiresMinutes { get; set; }
}
///
/// 存放Token 跟过期时间的类
///
public class TnToken
{
///
/// token
///
public string TokenStr { get; set; }
///
/// 过期时间
///
public DateTime Expires { get; set; }
}
///
/// token工具类的接口,方便使用依赖注入,很简单提供两个常用的方法
///
public interface ITokenHelper
{
///
/// 根据一个对象通过反射提供负载生成token
///
///
///
///
TnToken CreateToken(T user) where T : class;
///
/// 根据键值对提供负载生成token
///
///
///
TnToken CreateToken(Dictionary keyValuePairs);
///
/// Token验证
///
/// token
/// 自定义各类验证; 是否包含那种申明,或者申明的值
///
bool ValiToken(string encodeJwt, Func, bool> validatePayLoad = null);
///
/// 带返回状态的Token验证
///
/// token
/// 自定义各类验证; 是否包含那种申明,或者申明的值
///
///
TokenType ValiTokenState(string encodeJwt, Func, bool> validatePayLoad, Action> action);
}
///
/// Token生成类
///
public class TokenHelper : ITokenHelper
{
private readonly IOptions _options;
public TokenHelper(IOptions options)
{
_options = options;
}
///
/// 根据一个对象通过反射提供负载生成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 CreateToken(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]));
var payLoad = JsonConvert.DeserializeObject>(Base64UrlEncoder.Decode(jwtArr[1]));
//配置文件中取出来的签名秘钥
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);
}
///
///
///
///
///
///
///
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]));
var payLoad = JsonConvert.DeserializeObject>(Base64UrlEncoder.Decode(jwtArr[1]));
var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(_options.Value.IssuerSigningKey));
//验证签名是否正确(把用户传递的签名部分取出来和服务器生成的签名匹配即可)
if (!string.Equals(jwtArr[2], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArr[0], ".", jwtArr[1]))))))
{
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))
{
return TokenType.Fail;
}
//可能需要获取jwt摘要里边的数据,封装一下方便使用
action(payLoad);
return TokenType.Ok;
}
}
public enum TokenType
{
Ok,
Fail,
Expired
}
}