如何使用.NetCore自带类库实现JSON Web Token 认证【三】如何使用通道过滤器验证 JSON Web Token
使用通道过滤器校验:请求是否携带JWT,JWT是否被修改,JWT是否在有效期
目录:
一:什么是JSON Web Token
二:如何获取JSON Web Token
三:如何使用通道过滤器验证 JSON Web Token
Token服务的接口与实例
在Token服务的接口与实例中添加Validate函数用来校验JWT
Token服务
/*-------------------------------------------------------------------------
* 作者:WuTian
* 版本号:v1.0
* 本类主要用途描述及食用方式:
* JsonWebToken接口
* -------------------------------------------------------------------------*/
using EasyCore.Model;
namespace EasyCore.BLL
{
public interface ITokenService
{
/// <summary>
/// 将一个Model作为额外数据,生成token
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="user"></param>
/// <returns></returns>
LoginViewModel CreateToken<T>(T user) where T : class;
/// <summary>
/// 创建 TOKEN 无负载数据
/// </summary>
/// <returns></returns>
LoginViewModel CreateToken();
/// <summary>
/// 验证身份 验证签名的有效性
/// </summary>
/// <param name="encodeJwt"></param>
void Validate(string encodeJwt);
}
}
Token实例
namespace EasyCore.BLL
{
public class TokenService : ITokenService
{
#region 依赖注入
private readonly IOptions<JwtConfigModel> _options;
public TokenService(IOptions<JwtConfigModel> options)
{
_options = options;
}
#endregion
#region 生成Token
//创建 TOKEN 无负载数据
public LoginViewModel CreateToken()
{
List<Claim> claims = new List<Claim>();
return CreateToken(claims);
}
/// <summary>
/// 通过反射将一个Model作为负载数据,生成token
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="user"></param>
/// <returns></returns>
public LoginViewModel CreateToken<T>(T user) where T : class
{
//携带的负载数据,类似一个键值对
List<Claim> claims = new List<Claim>();
//填充负载数据的键值对
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);
}
private LoginViewModel CreateToken(List<Claim> claims)
{
DateTime now = DateTime.Now;
DateTime expires = now.Add(TimeSpan.FromMinutes(_options.Value.AccessTokenExpiresMinutes));
JwtSecurityToken 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 LoginViewModel { TokenStr = new JwtSecurityTokenHandler().WriteToken(token), Expires = expires };
}
#endregion
#region 校验Token
/// <summary>
/// 验证身份 验证签名的有效性
/// </summary>
/// <param name="encodeJwt"></param>
public void Validate(string encodeJwt)
{
string[] jwtArr = encodeJwt.Split('.');
Dictionary<string, string> header = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[0]));
Dictionary<string, string> payLoad = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[1]));
//配置文件中取出来的签名秘钥
HMACSHA256 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]))))))
{
throw new Exception("JsonWebToken不正确");
}
//其次验证是否在有效期内
long now = ToUnixEpochDate(DateTime.UtcNow);
if (now < long.Parse(payLoad["nbf"].ToString()) || now > long.Parse(payLoad["exp"].ToString()))
{
throw new Exception("JsonWebToken不在有效期");
}
}
private long ToUnixEpochDate(DateTime date) =>
(long)Math.Round((date.ToUniversalTime() - new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero)).TotalSeconds);
#endregion
}
}
通道过滤器
跳过JWT校验的特性
使用此特性用来忽略JWT校验
/*-------------------------------------------------------------------------
* 作者:WuTian
* 版本号:v1.0
* 本类主要用途及食用方式:
* 使用此特性用来忽略JWT校验
* -------------------------------------------------------------------------*/
using System;
namespace EasyCore.API
{
public class JsonWebTokenValidateIgnoreAttribute : Attribute
{
}
}
用来做JWT校验的特性
namespace EasyCore.API
{
public class JsonWebTokenValidateAttribute : ActionFilterAttribute
{
#region 服务依赖
private readonly ITokenService tokenService;
public JsonWebTokenValidateAttribute(ITokenService _tokenService)
{
tokenService = _tokenService;
}
#endregion
public override void OnActionExecuting(ActionExecutingContext context)
{
//如果有忽略JsonWebToken校验的特性 则不进行校验
if (context.ActionDescriptor.EndpointMetadata.Any(item => item is JsonWebTokenValidateIgnoreAttribute))
{
return;
}
string authHeader = context.HttpContext.Request.Headers["Authorization"];//Header中的token
if (authHeader == null || authHeader.IndexOf("Bearer ") < -1)
{
throw new Exception("没获取到正确的JsonWebToken");
}
authHeader = authHeader.Replace("Bearer ", "");
tokenService.Validate(authHeader);
}
}
}
控制器
namespace EasyCore.API.Controllers
{
public class LoginController : BaseController
{
#region 服务依赖
private readonly ITokenService tokenService;
public LoginController(ITokenService _tokenService)
{
tokenService = _tokenService;
}
#endregion
/// <summary>
/// 登录接口 跳过JWT校验 以及获取JWT
/// </summary>
/// <param name="paraModel"></param>
/// <returns></returns>
[JsonWebTokenValidateIgnore]
public ActionResult Login(LoginParaModel paraModel)
{
//根据用户名和密码去数据库查询,判断用户是否存在,判断密码是否正确。并获取用户ID
if (paraModel.UserLoginName == "NoUser")
{
throw new Exception("当前用户不存在");
}
//负载数据
JwtClaimModel claimModel = new JwtClaimModel
{
UserId = "10001"
};
//负载数据的JWT
LoginViewModel viewModel = tokenService.CreateToken(claimModel);
//返回前端
return JsonResult(viewModel);
}
//不跳过JWT校验
public ActionResult JwtDemo()
{
//返回前端
return JsonResult("成功");
}
}
}
测试
调用Login接口,获取本用户的TOKEN

调用含有JWT校验的业务接口
含有有效TOKEN的调用

无效TOKEN的情况

Token过期的情况
调整系统时间为1天后


浙公网安备 33010602011771号