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
协议。