JWT (Json Web Token)
起源: 说到JWT, 我们应该探讨一下传统session鉴权与现在常用的token鉴权有何不同
传统Session鉴权:
我们知道http协议是无状态协议, 即每一次请求之间是没有关联的, 对于服务器来说每一次请求犹如一个陌生人来访. 那我们为了记住我们的登录状态, 我们需要在用户进行用户名密码验证之后产生一个特殊字符串, 并利用cookie保存到客户浏览器上, 并且服务器上也要保存一下这个特殊字符串并与客户信息有个映射关系. 那么下次客户再访问服务器就会带上特殊字符串,服务器通过映射关系能够取到客户信息, 这样服务器就知道了这次的客户是谁, 也就记住了客户的登录状态. 那么这个特殊字符串就叫session.
传统session暴露的缺点:
上边我们说了session需要在服务器保存一份并且与客户信息做映射, 通常session信息都是存在内存中的, 随着客户认证越来越多, 服务端开销会越来越大,而且因为session存在内存中, 客户每次来认证都要访问这台服务器, 这样在分布式的应用上, 相应的限制了负载均衡的能力, 也限制了应用的扩展能力.
说完session我们开始说token
token跟session最大的变化就是token不需要再服务端存储任何东西了, 这意味着我们不必在意客户去哪台服务器上去认证了, 对于分布式的应用来说更自由了. token的客户认证信息都存储在token字符串里.接下来我们了解一下jwt-token的组成.
JWT的组成:
JWT长什么样
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
第一部分叫头部(header), 第二部分叫载荷(payload), 第三部分叫签证(signature)
header部分我们通常会包含类型声明(jwt类型)及加密算法声明(通常采用HMAC Sha256加密算法), 一个完整的header的json长这样:
{
'typ': 'JWT',
'alg': 'HS256'
}
然后对header部分进行Base64转码,生成JWT的第一部分密文
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
payload部分包含我们自定义的一些数据,如用户的非敏感信息(用户名,用户id等等)
我们定义一个payload, 此处不建议放客户敏感信息, 因为这一部分也是Base64转码, 是可以被逆向的.
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
对payload部分进行Base64转码得到JWT第二部分的密文
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
第三部分是签证部分, 需要用header(Base64后的), payload(Base64后的) 用"."连接组成字符串. 然后用声明好的加密方式对这个字符串进行加盐 secret(加密盐值) 加密. 最终生成JWT第三部分
用代码表示这个流程如下:
// javascript
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
最终把这三部分用"."连接起来, 就是最终的JWT-token
提醒: 上边用到的 secret 是保存在服务端的, 任何场景下都不能透漏出去, 否则客户端就有了自主签发JWT的能力.
JWT 如何验证:
客户端每次向服务器发送请求都要带上这个JWT-token, 服务端收到后, 重新进行加密计算 签证(signature) 部分, 如果与客户端发送过来的签证一致的话, 则通过验证. 由此可以保证这个JWT一定是服务端签发出去的而且没有被修改.