参数传递机制之JWT

1. 什么是 JWT

JWT 其全称为:JSON Web Token,简单地说就是 JSON 在 Web 上的一种带签名的标记形式。官方的定义以下:html

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.算法

即:JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于做为JSON对象在各方之间安全地传输信息。api

2. 有什么做用

对信息进行签名以后再进行传输有什么做用,JWT 就有什么做用。它能起的做用,决定了在项目的需求中是否有必要使用它,它自身的本质决定了它适合的场景。数组

本质上,JWT 跟本身对信息加个签名没有区别。安全

那使用它的理由是什么呢?ide

(1)它创建了一个标准并为多数人认识和接受,这样一来就能够造成标准库,使用者能够共享。函数

(2)它造成了一些最佳实践,这种实践过程包括了参数安全传递的诸多常见方面,如 exp 到期时间属性的定义来规定签名有效期等。按照最佳实践中对一些 JSON 属性的明肯定义,再加上标准库对它的贯彻实现,会带来不少便利。工具

(3)将其做为 Token 放在请求的 header 中,做为无状态的鉴权方式很适合目前多站点应用的场景。测试

但最佳实践和其特性不能混为一谈,具体到应用场景,仍然能够利用其特性做适合该场景的其它发挥。编码

3. 参数访问控制演化

(1)直接传参

http://*/api?p1=*&p2=*

这种方式,不进行访问的权限的判断,公开可直接访问。

(2)带KEY传参

http://*/api?p1=*&p2=*&key=

这种方式须要知道正确的 KEY 才能访问,但 KEY 明文附在后面易泄露。

(3)带签名传参

这种方式,将 KEY 做为签名算法的加密条件,不明文显示,不知道 KEY 则没法生成相应的签名,感受不错。不足在于,签名一次以后访问连接一直为有效会带有风险。

http://*/api?p1=*&p2=*&sign=

其中签名部分,如采用 md5 方式,key 做为运算的一部分。

sign=md5(p1+p2+key)

(4)带时间戳签名

参数中带上签名时的时间戳,时间戳会参与签名算法,服务端不只检测签名的有效性,还会比较时间是否在合理范围内,如 5 分钟之内,如此一来,连接在一段时间以后就会失效。

http://*/api?p1=*&p2=*&timestamp=*&sign=

其中签名部分,如采用 md5 方式,time,key 均做为运算的一部分。

sign=md5(p1+p2+time+key)

(5)独立鉴权参数签名

将鉴权部分独立出来签名,这样的好处就是鉴权部分独立的判断过程,其它形参再也不须要参与这个签名与判断过程。

参数可以使用 JSON 形式,因而可让其变成如下形式:

鉴权传输部分形式如:{p1:abcd,p2:abcd}.sign

其中,签名部分,如采用 md5 方式,将 JSON 字符串与 key 拼接运算,而且使用链接符.点,以下。

sign=md5({p1:abcd,p2:abcd}.key)

(6)带头部的独立鉴权部分

为了更加灵活的,将鉴权部分加个头部。头部用来干什么呢,能够指定签名算法,或之后可能要更多扩展参数用,如如下形式。

{alg:MD5}.{p1:abcd,p2:abcd}.sign

签名部分,为前两部分再链接上 key 一块儿运算。

sign=md5({alg:MD5}.{p1:,p2:}.key)

(7)最终标准化为 JWT 形式

头部称之为 header,数据部分称之为 payload,签名部分为 signature。

(7.1) header 不使用明文,采用其 base64 形式

(7.2) payload 不使用明文,采用其 base64 形式

(7.3) signature 为前二者(都是 base64 形式)经过 . 点链接,再采用 header 中指定的签名算法签名的结果。

(7.4) 最终形式为 base64(header).base64(payload).signature

(7.5) base64 考虑到URL编码,将=去掉,+号变成-,/变成_ 处理。

(7.6) 最终字符串经过做为请求 header 进行传输。

4. 最简实现

给定一个签名用的 sercretKey 和 payload,生成成符合要求的 JWT 字符串。多数时候,需求可能就是这样简单,至于签名算法,这里就使用通常默认的HS256。则须要的功能函数大体是:

func getJwt (payload){
    var content = base64({"alg":"HS256","typ":"JWT"}) + . + base64(payload)
    var signature = base46( sign(content, sercretKey)  )
    return content + . + sign
}

C# 的实例代码,这里给出一个 C# 的 JWT 辅助类,其中 JObject 引用了 Newtonsoft.Json 包。

public class JWTHelper
    {
        #region 工具函数准备

        /// <summary>
        /// 对字符串 Base64 编码,而且替换 = + / 为 "" - _
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string Base64URL(string str)
        {
            return Convert.ToBase64String(Encoding.UTF8.GetBytes(str)).Replace("=", "").Replace("+", "-").Replace("/", "_");
        }

        /// <summary>
        /// 对字节数组 Base64 编码,而且替换 = + / 为 "" - _
        /// </summary>
        /// <param name="bs"></param>
        /// <returns></returns>
        public static string Base64URL(byte[] bs)
        {
            return Convert.ToBase64String(bs).Replace("=", "").Replace("+", "-").Replace("/", "_");
        }

        /// <summary>
        /// HMAC SHA256
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string HS256(string str, string key)
        {
            var encoding = new System.Text.UTF8Encoding();
            byte[] keyByte = encoding.GetBytes(key);
            byte[] messageBytes = encoding.GetBytes(str);
            using (var hmacsha256 = new HMACSHA256(keyByte))
            {
                byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
                return Base64URL(hashmessage);  
            }
        }

        #endregion

        /// <summary>
        /// 生成签名后的 JWT 最终字符串。
        /// 为了简化示例,这里使用签名算法就设定为:HS256
        /// header = {"alg":"HS256","typ":"JWT"}
        /// </summary>
        /// <param name="payload"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static string Sign(JObject payload, String key)
        {
            JObject header = new JObject();
            header["alg"] = "HS256";
            header["typ"] = "JWT";

            string h = Base64URL(header.ToString(Formatting.None));
            string p = Base64URL(payload.ToString(Formatting.None));

            string s = HS256(h + "." + p, key);
            return String.Format("{0}.{1}.{2}", h, p, s);
        }
    }

使用如下代码测试一下:

JObject payload = new JObject();
payload["username"] = "xxx";
Console.Write(JWTHelper.Sign(payload, "123fd"));

获得结果

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Inh4eCJ9.1C28A5CqMa70FLtUQh4pwSZWPlZhbQ-ZeYs38K_sqks

https://jwt.io/ 上,能够验证一下,获得了一样的结果。

5. 具体使用

显然,它也会存在一些问题,如经过 base64 解码看到明文,或者是在有效期内取得整个 token 进行访问等。因此使用是根据须要来的。并且,也能够在 JWT 上进一步加入自定义的新机制来应对更多的场景。

如下这篇文章列出了一些问题与趋势,可供参考。

https://baijiahao.baidu.com/s?id=1608021814182894637

更多细节可参考:http://www.javashuo.com/article/p-zuvhvdiv-cy.html

相关文章
相关标签/搜索