一、JWT简介
JWT(JSON WEB TOKEN)是一个基于JSON的公开规范(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息使用了数字签名,可以被验证与信任。
二、应用场景
- Authorization(授权):JWT最常见的应用场景,一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用。
- Information Exchange (信息交换) : JWT也可用于在各方之间安全的传输信息 。因为JWT使用了数字签名。例如,使用公钥/私钥对,就可对发送人加以验证。另外,签名是使用请求头和求求内容计算得到的,因此还可以验证内容没有被篡改。
三、JWT的结构
JSON Web Token由三部分组成,它们之间用圆点(.)连接。这三部分分别是:
- Header
- Payload
- Signature
一个典型的JWT的形式如下所示:
aaaa.bbbbbbbbbb.cccccc
具体到每一部分:
1. Header
Header典型的由两部分组成:token的类型(“JWT”)和算法名称(比如:HMAC SHA256或者RSA等等)。
{
'alg': "HS256",
'typ': "JWT"
}
然后,用Base64对这个JSON编码就得到JWT的第一部分.
2. Payload
JWT的第二部分是payload,它是关于实体(通常是用户)和其他数据的声明。声明有三种类型: registered, public 和 private。
- Registered claims : 这里有一组预定义的声明,使用时推荐,但不强制。比如:iss (issuer 发布者), exp(expiration time 有效期), sub (subject 主题), aud (audience)等。
- Public claims : 此类型声明可以随意定义。
- Private claims : 不属于公开或者注册的声明,用在各方约定好并同意在各方之间共享信息所使用的声明。
一个示例:
{
"exp":1625309676,
"user_name":"liuzhihao",
"jti":"2ddc8367-b4ba-448b-a282-c52cc04c289d",
"client_id":"browser",
"scope":[
"all"
],
"ip":"172.21.72.116"
}
用Base64对Payload这个JSON编码就得到JWT的第二部分。
由于base64可被解码因而被获取到所包含的信息,因此不建议在payload或header中放置敏感信息,除非它们是加密的。
3. Signature
将编码过的header与编码过的payload使用header所声明的算法名称对它们签名,即可得到第三部分。
格式示例:
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
签名用于验证消息在传递过程中有没有被更改,而且对于使用私钥签名的token,它还可以验证JWT的发送方的身份。
生成一条完整的JWT示例:
四、JWT的使用
在认证时,当用户用他们的凭证成功登录以后,会获取到一个返回的JWT。此后,Token就是用户凭证了,但必须小心防止出现安全问题。一般而言,Token有效期不应超过你需要使用它的时间。
无论何时用户想要访问受保护的路由或者资源的时候,用户代理(通常是浏览器)都应该带上JWT。通常JWT放在Authorization header中,用bearer schema。
header通常为这种形式:
Authorization: Bearer
服务器上的受保护的路由将会检查Authorization header中的JWT是否有效。如果有效,则用户可以访问受保护的资源。如果JWT包含着足够多的必要的求取数据,那么就可以减少服务端去数据库查询的很多操作。
如果token是在授权头(Authorization header)中发送的,那么跨源资源共享(CORS)将不会成为问题,因为它不使用cookie。
五、 基于Token的身份认证 与 基于服务器的身份认证
1.基于服务器的身份认证
HTTP协议是无状态的。也就是说,我第一次登陆时已经认证了一个用户,但是当我第二次登陆发送请求的时候,服务器依然不知道我是谁,我必须再次认证。
传统的做法是将已经认证过的用户信息存储在服务器上,比如Session。用户下次请求的时候带着Session ID,然后服务器检查Session 确认用户是否认证过。
这种基于服务器的身份认证方式存在一些问题:
- Sessions :
每次用户认证通过以后,服务器需要创建一条记录保存用户信息,通常是在内存中,随着认证通过的用户越来越多,服务器的在这里的开销就会越来越大。 - Scalability : 由于Session是在内存中的,这就带来一些扩展性的问题。
- CORS :
当我们想要扩展我们的应用,让我们的数据被多个移动设备使用时,我们必须考虑跨资源共享问题。当使用AJAX调用从另一个域名下获取资源时,我们可能会遇到禁止请求的问题。 - CSRF : 用户很容易受到CSRF攻击。
2.JWT与Session的异同
- JWT和Session都是存储用户信息的载体,但是Session是存储在服务器端的,而JWT是存储在客户端的。
- 方式存储用户信息的最大问题在于要占用大量服务器内存,增加服务器的开销;而JWT方式将用户状态分散到了客户端中,可以明显减轻服务端的内存压力。
- Session的状态存储在服务器端,客户端只有session id;而Token的状态存储在客户端。
3.基于Token的身份认证
基于Token的身份认证是无状态的,服务器中不会存储任何用户信息。
没有会话信息意味着应用程序可以根据需要扩展和添加更多的机器,而不必担心用户登录的位置。
这一实现的主要流程如下:
- 用户携带用户名和密码请求访问
- 服务器校验用户凭据
- 应用提供一个token给客户端
- 客户端存储token,并且在随后的每一次请求中都带着它
- 服务器校验token并返回数据
注意:
- 每一次请求都需要token
- Token应该放在请求header中
- 我们还需要将服务器设置为接受来自所有域的请求,用Access-Control-Allow-Origin: *
4.Token的优势
- 无状态和可扩展性:Tokens存储在客户端。完全无状态,可扩展。我们的负载均衡器可以将用户传递到任意服务器,因为在任何地方都没有状态或会话信息。
- 安全:Token并不是Cookie,每次请求的时候Token都会被发送。而且,由于没有Cookie被发送,还有助于防止CSRF攻击。即使在你的实现中将token存储到客户端的Cookie中,这个Cookie也只是一种存储机制,而非身份认证机制。没有基于会话的信息可以操作,因为我们没有会话。
还有一点,token在一段时间以后会过期,这个时候用户需要重新登录。这有有利于安全性。还有一个概念叫token撤销,它允许我们根据相同的授权许可使特定的token甚至一组token无效。
5. JWT与OAuth的区别
- OAuth2是一种授权框架 ,JWT是一种认证协议
- 无论使用哪种方式切记用HTTPS来保证数据的安全性
- OAuth2用在使用第三方账号登录的情况(比如使用weibo, qq, github登录某个app),而JWT是用在前后端分离, 需要简单的对后台API进行保护时使用。