1.用户认证的状况
在这一部分我们会介绍HTTP协议的无状态性,以及目前常见的用户认证方式。
1.1 HTTP 协议的无状态性
我们先来说说什么HTTP协议,它是HyperText Transfer Prototal(超文本传输协议)的简写。HTTP协议是互联网数据通讯的基础协议。HTTP协议的无状态性,对现在的我们来讲有些费解,但是在当时都是那么的顺其自然。HTTP协议最初的设计目的是提供一种发布和接受HTML页面的方法,主要用于百科全书,在线文档,教程,新闻等等,在HTTP/0.9协议种更是只支持GET请求。简单地说就是获取信息,不需要认证授权,不要跟踪用户行为,两次请求之间也没有关联。因此HTTP协议的无状态性可以说是非常合理。
1.2 基于Session的交互式网站
随着互联网的发展,HTTP协议的无状态性的缺点也变得更加突出,严重地阻碍了交互式 Web 应用的发展。比如一个网站的信息不是公开的,只对会员开放,这就要求用户在访问页面的时候必须提供用户名和密码,因为HTTP协议的无状态性,就要每访问一个页面就要输入一次用户名密码,用户一定会疯掉。因此session的概念就应运而生了。那么用户放文网站的流程变成这样:
- 用户输入网站的某个页面
- 服务器判断用户尚未登录(没有提供session_id),跳转到登录页面。
- 用户输入用户名和密码
- 服务器端通过验证以后,给当前会话创建一个session_id,并往当前会话中保存用户信息。服务器端向浏览器返回一个session_id,并写入用户的Cookie(一般都是通过Cookie实现)。
- 用户再浏览页面时,session_id作为Cookie自动传递到服务器。
- 服务器在接受到session_id之后,获取此会话中的用户信息,直接返回页面内容。
一般我们会将会话保存到数据库或 Redis 等其他持久化服务中,为了以下两个原因:
- 如果将会话保存在内存中,那么服务器因为意外宕机或发布新版本重启时,导致会话丢失,进而导致用户需要重新登录。
- 为了应对高并发请求,我们一般首先水平扩展应用服务器,这时候就要求会话在多个服务器之间共享,持久化最好的方案。
这样会导致工程复杂以及持久层的单点故障。有什么方法避免这种问题吗?
2 JWT 的出现给我们带来另一种体验
2.1. JWT 是什么?
JWT发音jot([jät]),是JSON Web Token的简写。它是一种在两个网络应用主体之间传递声明的开放标准(RFC7519)。
JWT的原理是服务器完成认证之后将用户信息转换成一个字符串(以下称为token)以后返回给客户端,以后客户端发送请求时都会带上token,服务器直接从token中获取用户信息。你可能会问怎么防止用户信息被篡改呢?答案是给token中还包含签名。
JWT内容分为三个部分:头部Header, 负载Payload和签名Verify Signature。在https://jwt.io/网站中,如果你修改左侧token,网站即时解析token并将结束输出在右侧(如果成功的话),你修改了右侧明文,网站即时转换成token并更新在左侧。
2.2 头部Header
JWT头部是一个JSON字符串,描述JWT的元数据,比如alg字段表示采用哪种加密算法,typ字段表示令牌类型,JWT令牌统一为’JWT’。
| 1 | { | 
以上JSON字符串经过Base64URL算法处理以后就是JWT头部的最终内容了。
2.3 负载Payload
JWT负载部分也是一个JSON字符串,用来保存认证信息。JWT 协议中定义了以下 7 个官方字段,建议使用但不强制。
| 1 | { | 
除了官方字段以外,还可以存放其他自定义字段,比如
| 1 | { | 
和头部一样,以上JSON字符串经过Base64URL算法处理以后就是JWT头部的最终内容了。
特别注意,因为负载可以被客户端解析,因此不能存放诸如密码等敏感信息。
2.4 签名Verify Signature
签名Verify Signature是对前面两部分:头部Header和负载Payload的签名。
| 1 | HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), SECRET) | 
其中SECRET是秘钥。
2.5 Base64UrlEncode
前面提到数次的Base64Url算法是增强后的Base64算法,为什么不是直接使用Base64算法呢?其实JWT只是一般情况下通过Authorization请求头发送,而某些时间需要放在URL后面比如http://example.com/page?token=xxx,而Base64算法中的三个字符+, /, =在 URL 中有特殊含义,因此需要特殊,将+替换成-,/替换成_,=删除,这样就形成了Base64Url算法。
2.6 JWT 的传输方式
在上一小节中我们提供,JWT一般通过Authorization请求头发送,其结构是
| 1 | Authorization: Bearer <token> | 
也可以放在URL后面作为参数传输,也可以放在Cookie中传输。
2.7 JWT 的特点
- JWT默认是不加密的,但是也可加加密。生成原始- token以后,可以在用秘钥加密一次。
- JWT默认是不加密的,因此不能存放密码等敏感信息。
- JWT不仅可以用于认证,也可以用于交换信息。有效使用- JWT可以减少数据库查询次数。
- JWT最大缺点是由于服务器端不保存- session状态,因此无法在使用过程中废止- token,或者更改- token的权限。也就是说,一旦- JWT签发以后,在到期之前就一直有效,除非服务器端有额外的逻辑处理。
- JWT本身包含了认证信息,一旦泄露,所有人都可以获得该令牌,为了减少盗用,- JWT有效期应该设置的比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。
- 为了减少盗用,JWT不应该使用HTTP协议明码传输,要使用HTTPS协议。