小马经常看到招聘要求中会写着一个词:JWT。没接触过的同学可能一愣,这是啥高级玩意?看起来很唬人的样子,不能查,一查就是个token生成方式。好,那就来了解一下吧。
什么是JWT
JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
啥意思呢?就是说我是一种token生成方式/标准,可以用于身份认证也可以用于传递数据,并且我是可加密的。也就是要生产token的话,小马要是自己想规定一个token生成方式就不按JWT标准,也是没问题的。
为什么会有JWT这玩意呢
我们应该先来谈一谈传统的session认证和基于token的认证。
1、传统的session认证
因为HTTP协议本身是无状态的,而在本次会话过程中我们的每一次请求都需要进行用户身份认证的保持,于是将第一次请求服务端校验后的用户身份标志(session_id)保存在客户端(cookie)便于下次请求时带上身份,这样服务端就能识别身份了。借用一个图片来理解。
这种方式的弊端在于,因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造(CSRF)的攻击。由于直接依赖于cookie和session不利于作单点登录SSO服务(解决不同域名下的会话共享,跨域CORS)和没有cookie机制的(APP)的实现。【单点登录可分基于WEB的SSO(又分跨域和不跨域)和基于APP无cookie的SSO。借用几张图来便于理解SSO。】
token在单点登录SSO中的地位
因为基于sessionID的话,在不同的域名下是不同的会话ID,多个会话ID导致底层session数据不能唯一ID共享,而如果使用同一token标识去认证会话就可以达到共享。而其实可以理解为由SSO服务作为底层登录校验和用户数据存储,登录成功则生产token,分发到各个站点(主要是域名)的cookie里。各个站点登陆后带这个token去SSO服务校验登录态是否正常,登出时SSOtoken生效同时让各个cookie里的token设置过期。【就是用token统一到我这里来服务校验登录就对了】此时没有session校验环节或者说这个环节被SSO服务代替了。这个过程其实还是会有各自的session_id,因为产生会话。
基于web站点不跨域SSO登录
基于web站点不跨域SSO登录态校验
基于web站点不跨域SSO登出
基于web站点跨域SSO,test主域名未登录
埋一个彩蛋:如果禁用了cookie,session机制还可以用吗?可以,把session_id放请求参数里。session的存储方式有哪些?默认存文件,还可以存DB和cache。如果就是要基于session实现单点登录呢,即一处登录其他系统都能用?同域下,将session存储共享即可。
2、基于token的鉴权
基于token机制不需要知道用户在哪台机器登陆,服务端只认token标志。可以支持CORS跨域,不过服务端要同时支持,一般设置Access-Control-Allow-Origin: *。借JWT的一张图来助于理解。JWT其实就是token机制的一种实现,下面再讲。
3、JWT原理
我们上面盘了一下得到,JWT其实就是个token生成方式。那它是怎么个生法呢?
JWT是由三段信息构成的,分别是Header,Payload,Signature,将这三段信息文本用点链接一起就构成了JWT字符串。看起来是这个样子:aaa.bbb.ccc。
Header典型的由两部分组成:token的类型(“JWT”)和算法名称(比如:HMAC SHA256或者RSA等等)。例如{'alg': "HS256",'typ': "JWT"},用Base64对这个JSON加密(是对称加密)就得到JWT的第一部分。
Payload载荷就是存放有效信息的地方(可以理解为皮卡的后备箱)。有效信息包含三个部分:标准中注册的声明(这里有一组预定义的声明,它们不是强制的,但是推荐。比如:iss (issuer), exp (expiration time), sub (subject), aud (audience)等),公共的声明(可以随意定义任何信息),私有的声明(是提供者和消费者所共同定义的声明,用于在同意使用它们的各方之间共享信息)。这三部分信息都不建议放敏感信息,因为是base64对称加密的,除非信息本身已经有做过一层加密处理。第二段信息格式例子如下:{"sub": '123456789',"name": 'xiaoma',"level":'1'},同样进行Base64编码就得到JWT的第二部分。
Signature签名(可以理解为驾照),对编码过的Header、编码过的Payload、一个密钥(私钥),签名算法用是Header中指定的那个,然对它们签名得到的就是JWT的三部分。类似Signature=HS256(base64(Header)+base64(Payload),secret)。私钥可以防止篡改和验证有效性。
实践演练
好了,我们得到了一个JWT方式的token。那这个token放哪里呢? query参数吗?最好把JWT放在HTTP请求的Header Authorization,格式是Authorization: Bearer jwtStr。类似如下:
设置Header Authorization
查看结果请求头
服务端获取到请求头JWT数据
后记
笔者认为,JWT也就是个token生成方式和普通token本质没啥区别。要是JWT串被截取和普通token一样同样逃不掉权限被劫持,最好用HTTPS加密请求。为了安全,服务端对于敏感权限需要重新验证,令牌要设置合理的过期时间。
JWT不仅仅可以实现身份认证还可以在跨域post请求时将请求参数加入到有效载荷中,实现post跨域请求。
本文主要助于理解,水平有限,不到之处欢迎指正。
参考:
五分钟带你了解啥是JWT