载荷实体:
/// <summary> /// JWT载荷实体 /// </summary> public sealed class JWTPlayloadInfo { /// <summary> /// jwt签发者 /// </summary> public string iss { get; set; } = "Berry.Service"; /// <summary> /// jwt所面向的用户 /// </summary> public string sub { get; set; } = "ALL"; /// <summary> /// 接收jwt的一方 /// </summary> public string aud { get; set; } = "guest"; /// <summary> /// jwt的签发时间 /// </summary> public string iat { get; set; } = DateTimeHelper.GetTimeStamp(DateTime.Now).ToString(); /// <summary> /// jwt的过期时间,这个过期时间必须要大于签发时间.默认60分钟 /// </summary> public string exp { get; set; } /// <summary> /// 定义在什么时间之前,该jwt都是不可用的. /// </summary> public int nbf { get; set; } /// <summary> /// jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。 /// </summary> public string jti { get; set; } = CommonHelper.GetGuid(); /// <summary> /// 用户ID。自定义字段 /// </summary> public string userid { get; set; } /// <summary> /// 扩展字段。自定义字段 /// </summary> public string extend { get; set; } }
JWTHelper.cs:
/// <summary> /// JWT操作帮助类 /// </summary> public sealed class JWTHelper { /// <summary> /// 签发Token /// </summary> /// <param name="playload">载荷</param> /// <returns></returns> public static string GetToken(JWTPlayloadInfo playload) { string token = String.Empty; IJwtAlgorithm algorithm = new HMACSHA256Algorithm(); IJsonSerializer serializer = new JsonNetSerializer(); IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder(); IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder); //设置过期时间 DateTime time = DateTime.Now.AddMinutes(120); playload.exp = DateTimeHelper.GetTimeStamp(time).ToString(); Dictionary<string, object> dict = playload.Object2Dictionary(); //获取私钥 string secret = GetSecret(); //将Token保存在缓存中 if (!string.IsNullOrEmpty(playload.aud) && playload.aud.Equals("guest")) { //计算公用Token token = CacheFactory.GetCacheInstance().GetCache("JWT_TokenCacheKey:Guest", () => { return encoder.Encode(dict, secret); }, time); } else { //计算Token token = CacheFactory.GetCacheInstance().GetCache($"JWT_TokenCacheKey:{playload.aud}", () => { return encoder.Encode(dict, secret); }, time); } return token; } /// <summary> /// Token校验 /// </summary> /// <param name="token"></param> /// <returns></returns> public static JWTPlayloadInfo CheckToken(string token) { if (string.IsNullOrEmpty(token)) return null; IJsonSerializer serializer = new JsonNetSerializer(); IDateTimeProvider provider = new UtcDateTimeProvider(); IJwtValidator validator = new JwtValidator(serializer, provider); IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder(); IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder); //获取私钥 string secret = GetSecret(); JWTPlayloadInfo playloadInfo = decoder.DecodeToObject<JWTPlayloadInfo>(token, secret, true); if (playloadInfo != null) { if (!string.IsNullOrEmpty(playloadInfo.aud) && playloadInfo.aud.Equals("guest")) { string cacheToken = CacheFactory.GetCacheInstance().GetCache<string>("JWT_TokenCacheKey:Guest"); return Check(playloadInfo, cacheToken, token) ? playloadInfo : null; } else { string cacheToken = CacheFactory.GetCacheInstance().GetCache<string>($"JWT_TokenCacheKey:{playloadInfo.aud}"); return Check(playloadInfo, cacheToken, token) ? playloadInfo : null; } } return null; } private static bool Check(JWTPlayloadInfo info, string cacheToken, string token) { if (string.IsNullOrEmpty(cacheToken)) return false; if (string.IsNullOrEmpty(token)) return false; if (!cacheToken.Equals(token)) return false; //Token过期 DateTime exp = DateTimeHelper.GetDateTime(info.exp); if (DateTime.Now > exp) { if (!string.IsNullOrEmpty(info.aud) && info.aud.Equals("guest")) { CacheFactory.GetCacheInstance().RemoveCache("JWT_TokenCacheKey:Guest"); } else { CacheFactory.GetCacheInstance().RemoveCache($"JWT_TokenCacheKey:{info.aud}"); } return false; } return true; } /// <summary> /// 获取私钥 /// </summary> /// <returns></returns> private static string GetSecret() { //TODO 从文件中去读真正的私钥 return "eyJpc3MiOiJCZXJyeS5TZXJ2aWNlIiwic3ViIjoiMTgyODQ1OTQ2MTkiLCJhdWQiOiJndWVzdCIsImlhdCI6IjE1MzEzODE5OTgiLCJleHAiOiIxNTMxMzg5MTk4IiwibmJmIjowLCJqdGkiOiI1YzdmN2ZhM2E4ODVlODExYTEzNTQ4ZDIyNGMwMWQwNSIsInVzZXJpZCI6bnVsbCwiZXh0ZW5kIjpudWxsfQ"; } }
自定义忽略验证特性:
/// <summary> /// 忽略验证 /// </summary> [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] public class IgnoreTokenAttribute : Attribute { public bool Ignore { get; set; } /// <summary> /// 忽略验证.默认忽略 /// </summary> /// <param name="ignore"></param> public IgnoreTokenAttribute(bool ignore = true) { this.Ignore = ignore; } }
自定义Action拦截器,处理验证逻辑:
public class CustomActionFilterAttribute : ActionFilterAttribute { /// <summary>在调用操作方法之前发生。</summary> /// <param name="actionContext">操作上下文。</param> public override void OnActionExecuting(HttpActionContext actionContext) { string isInterfaceSignature = ConfigHelper.GetValue("IsInterfaceSignature"); if (isInterfaceSignature.ToLower() == "false") return; BaseJsonResult<string> resultMsg = null; //授权码 string accessToken = string.Empty; //操作上下文请求信息 HttpRequestMessage request = actionContext.Request; //数字签名数据 if (request.Headers.Contains("Authorization")) { accessToken = HttpUtility.UrlDecode(request.Headers.GetValues("Authorization").FirstOrDefault()); } //接受客户端预请求 if (actionContext.Request.Method == HttpMethod.Options) { actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Accepted); return; } //忽略不需要授权的方法 var attributes = actionContext.ActionDescriptor.GetCustomAttributes<IgnoreTokenAttribute>(); if (attributes.Count > 0 && attributes[0].Ignore) return; //判断请求头是否包含以下参数 if (string.IsNullOrEmpty(accessToken)) { resultMsg = new BaseJsonResult<string> { Status = (int)JsonObjectStatus.ParameterError, Message = JsonObjectStatus.ParameterError.GetEnumDescription() }; actionContext.Response = resultMsg.ToHttpResponseMessage(); return; } //校验Token是否有效 JWTPlayloadInfo playload = JWTHelper.CheckToken(accessToken); if (playload == null) { resultMsg = new BaseJsonResult<string> { Status = (int)JsonObjectStatus.TokenInvalid, Message = JsonObjectStatus.TokenInvalid.GetEnumDescription() }; actionContext.Response = resultMsg.ToHttpResponseMessage(); return; } else { //校验当前用户是否能够操作某些特定方法(比如更新用户信息) if (!attributes[0].Ignore) { if (!string.IsNullOrEmpty(playload.aud) && playload.aud.Equals("guest")) { resultMsg = new BaseJsonResult<string> { Status = (int)JsonObjectStatus.Unauthorized, Message = JsonObjectStatus.Unauthorized.GetEnumDescription() }; actionContext.Response = resultMsg.ToHttpResponseMessage(); return; } } } base.OnActionExecuting(actionContext); } }
在WebApiConfig.cs中注册:
config.Filters.Add(new CustomActionFilterAttribute());
新增获取Token控制器,添加获取Token方法:
/// <summary> /// 获取授权Token /// </summary> /// <param name="arg"></param> /// <returns></returns> [HttpPost] [IgnoreToken(true)] public HttpResponseMessage GetJWTToken(GetTokenArgEntity arg) { BaseJsonResult<string> resultMsg = this.GetBaseJsonResult<string>(); Logger(this.GetType(), "获取授权Token-GetJWTToken", () => { if (!string.IsNullOrEmpty(arg.t)) { //TODO 根据UserID校验用户是否存在 if (true) { JWTPlayloadInfo playload = new JWTPlayloadInfo { iss = "Berry.Service", sub = arg.Account, aud = arg.UserId }; string token = JWTHelper.GetToken(playload); resultMsg = this.GetBaseJsonResult<string>(token, JsonObjectStatus.Success); } else { resultMsg = this.GetBaseJsonResult<string>("", JsonObjectStatus.UserNotExist); } } else { resultMsg = this.GetBaseJsonResult<string>("", JsonObjectStatus.Fail, ",请求参数有误。"); } }, e => { resultMsg = this.GetBaseJsonResult<string>("", JsonObjectStatus.Exception, ",异常信息:" + e.Message); }); return resultMsg.ToHttpResponseMessage(); }
获取Token参数实体:
/// <summary> /// 获取Token参数 /// </summary> public class GetTokenArgEntity : BaseParameterEntity { /// <summary> /// 用户ID /// </summary> [Required(ErrorMessage = "UserId不能为空")] public string UserId { get; set; } /// <summary> /// 帐号 /// </summary> [Required(ErrorMessage = "Account不能为空")] public string Account { get; set; } }
原文地址:https://www.cnblogs.com/zhao-yi/p/9317835.html
时间: 2024-11-08 18:07:19