JWT技术——基于token的鉴权机制
2017年09月27日


本文是《REST API安全认证研究》的一部分。


JWT技术(基于token的鉴权机制)

流程上是这样的:

  1. 用户使用用户名密码来请求服务器

  2. 服务器进行验证用户的信息

  3. 服务器通过验证后生成一个token发送给用户

  4. 客户端存储token,并在每次请求时附送上这个token值

  5. 服务端验证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