看JWT,可以先看下网站jwt.io,这个网站可以解密你的token,解密后可以看到,一个JWT的token是由3部分构成:
1)Header(头),解密后格式:
{ "typ": "JWT", "alg": "HS256" }
2)PayLoad,需要注意的是,PayLoad解密后是明文存在的,比如:
{ "exp": 1536310394, "logininfo": { "UserId": "00000000-0000-0000-0000-000000000000", "UserName": "admin" } }
exp是什么时候过期,Unix时间戳。logininfo是放到里面的一个JSON实体。
3) 第三部分为签名。
在webapi中,我们先Nuget搜索JWT,然后使用下面代码生成token:
var adminLogin = new AdminLoginInfo() { UserId = Guid.Empty, UserName = "admin" }; string key = "ahuinan"; var payload = new Dictionary<string, object> { { "exp", DateTimeOffset.UtcNow.AddHours(1).ToUnixTimeSeconds() }, { "logininfo",JsonConvert.SerializeObject(adminLogin)} }; //采用HS256加密算法 IJwtAlgorithm algorithm = new HMACSHA256Algorithm(); IJsonSerializer serializer = new JsonNetSerializer(); IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder(); IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder); string token = encoder.Encode(payload, key);
接下来可以在webapi中添加一个过滤器验证token的有效性,
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext filterContext) { var attr = filterContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().OfType<AllowAnonymousAttribute>(); bool isAnonymous = attr.Any(p => p is AllowAnonymousAttribute); if (isAnonymous) { return; } var authHeader = from h in filterContext.Request.Headers where h.Key == "Authorization" select h.Value.FirstOrDefault(); bool successLogin = false; string errMsg = ""; if (authHeader != null) { //对token进行解密 try { IJsonSerializer serializer = new JsonNetSerializer(); IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder(); IDateTimeProvider provider = new UtcDateTimeProvider(); IJwtValidator validator = new JwtValidator(serializer, provider); IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder); var token = authHeader.FirstOrDefault().Replace("Bearer", "").Trim(); decoder.Decode(token, "ahuinan", verify: true); successLogin = true; } catch (Exception ex) { } } if (!successLogin) { Result r = new Result(); r.ErrorMessage = "请重新登录"; r.ResultCode = ResultCode.Error; filterContext.Response = filterContext.Request.CreateResponse<Result>(HttpStatusCode.OK, r, "application/json"); return; } }
当然,我们还需要在Controller层拿到用户名信息等呢,继续封装一下,根据token登陆的ID和用户名,代码如下:
public static T Get<T>(string token,string tokenKey,string jsonKey) { try { IJsonSerializer serializer = new JsonNetSerializer(); IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder(); IDateTimeProvider provider = new UtcDateTimeProvider(); IJwtValidator validator = new JwtValidator(serializer, provider); IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder); string json = decoder.Decode(token, tokenKey, verify: true); var dic = decoder.DecodeToObject<Dictionary<string, object>>(token); return JsonConvert.DeserializeObject<T>(dic[jsonKey].ToString()); } catch (TokenExpiredException ex) { throw new BaseException("请重新登陆,token已失效"); } catch (SignatureVerificationException ex) { throw new BaseException("请重新登陆,签名错误"); } }
那么过滤器中的完整代码就变成了:
var attr = filterContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().OfType<AllowAnonymousAttribute>(); bool isAnonymous = attr.Any(p => p is AllowAnonymousAttribute); if (isAnonymous) { return; } var authHeader = from h in filterContext.Request.Headers where h.Key == "Authorization" select h.Value.FirstOrDefault(); Result r = new Result(); bool successLogin = false; string errMsg = ""; if (authHeader != null) { try { var logininfo = TokenHelper.Get<LoginInfo>(authHeader.FirstOrDefault().Replace("Bearer", "").Trim(), SysGlobalCodeConst.TokenKey, SysGlobalCodeConst.LoginInfoKey); if (logininfo != null) { filterContext.ControllerContext.RouteData.Values[SysGlobalCodeConst.LoginInfoKey] = logininfo; } successLogin = true; } catch (Exception ex) { r.ErrorMessage = ex.Message; } } else { r.ErrorMessage = "请重新登录"; } if (!successLogin) { r.ResultCode = ResultCode.Error; filterContext.Response = filterContext.Request.CreateResponse<Result>(HttpStatusCode.OK, r, "application/json"); return; } }
Controller层取登陆信息的代码如下:
/// <summary> /// 取得登陆信息 /// </summary> protected LoginInfo CurrentUser { get { return this.RequestContext.RouteData.Values[SysGlobalCodeConst.LoginInfoKey] as LoginInfo; } }