1. 首页>
  2. 技术文章>
  3. .net在webapi中使用JWT做Token

.net在webapi中使用JWT做Token

9/8/18 4:42:13 PM 浏览 2363 评论 0

JWT token

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


网友讨论