本文是《REST API安全认证研究》的一部分。
JWT技术(基于token的鉴权机制)
流程上是这样的:
用户使用用户名密码来请求服务器
服务器进行验证用户的信息
服务器通过验证后生成一个token发送给用户
客户端存储token,并在每次请求时附送上这个token值
服务端验证token值,并返回数据
这个token必须要在每次请求时传递给服务端,它应该保存在请求头里, 另外,服务端要支持CORS(跨来源资源共享)策略,一般我们在服务端这么做就可以了Access-Control-Allow-Origin: *
JWT和Session技术的根本区别在于,Session是把访问者信息存储在服务器端的,而JWT是把信息存储在客户端。
Session方式存储用户信息的最大弊病在于要占用大量服务器内存,要保存许多的状态。一般而言,大型应用还需要借助缓存机制来实现Session的存储。
而JWT方式将用户状态分散到了客户端中,可以明显减轻服务端的内存压力。除了用户id之外,还可以存储其他的和用户相关的信息,例如该用户是否是管理员等。
JWT方式,每次请求都要带上token,服务器每次都要解密出token的信息,这会让服务器有一些计算压力。而且,如果客户端的信息比较多,每次HTTP请求携带的数据就比较多。还有一点,JWT在客户端的存储,不能包含敏感信息,否则很容易被泄露,而且不建议在客户端存储会变动的信息,否则可能出现一致性问题。
JWT长什么样?
JWT是由三段信息(header.payload.signature)构成的,将这三段信息文本用英文句号链接一起就构成了JWT字符串。例如:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
可以在 https://jwt.io/ 这个网站,把token解析出来。
JWT的构成
第一部分我们称它为头部(header),
第二部分我们称其为承载(payload, 类似于飞机上承载的物品),
第三部分是签证(signature)。
header
完整的头部就像下面这样的JSON:
{
'typ': 'JWT',
'alg': 'HS256'
}
即声明 类型 和 加密的算法。然后将头部进行base64加密,就构成了header部分。
playload
承载就是存放有效信息的地方。这个名字像是指飞机上承载的货品,这些有效信息包含三个部分声明(Claims)
标准中注册的声明
公共的声明
私有的声明
标准中注册的声明 (建议但不强制使用) :
iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
signature
signature是一个签证信息,它会用到前面的header、payload信息,以及一个密匙(secret)。
即,这个部分需要 base64加密后的header 和 base64加密后的payload,使用 英文句号 连接组成的字符串,然后通过header中声明的加密方式,进行加盐secret组合加密,然后就构成了jwt的第三部分signature。
JWT在实际应用中存在的问题
上面已经说到了两点,
1.每次请求都要携带token,服务器端都要解密和验证,对性能会有一定影响;
2.不适合存储敏感信息,容易泄露。
下面再总结几点:
3. token的存储一般有两种方式,一种是存在localStorage中,二是存在cookie中,这两种存储方式都有问题,存在localStorage中有很大的安全隐患,容易造成XSS攻击,因为跨站脚本可以读取localStorage里面的信息,如果存在cookie中,又容易造成CSRF攻击,这个是由于Cookie存储的安全性造成的(可以使用XSRF Token来解决这个问题)。
4. 失效和刷新问题,token肯定要有失效时间,而且如果用户一直处于活跃状态,则要考虑能自动刷新token。
5. 重放攻击,由于每次都传输token,如果这个token被窃取,那么可以在任何一个地方使用。特别的,如果这个token过期时间很长的话,那么它就相当于一个在很长时间内有效的钥匙。(可以考虑每次请求都重新生成一个token并存储一段时间防重放,但是这样又会遇到存储问题和并发刷新的问题,参见:https://zhuanlan.zhihu.com/p/22693223)
参考资料:
https://github.com/pac4j/spring-webmvc-pac4j
http://www.pac4j.org/docs/authenticators/jwt.html