JWT、JWT 以前看到过很多次,但仅知道他是一个认证方法而已。下面就来学习记录一下他的的原理和用法。
跨域认证
我们上网离不开用户认证,它的一般流程如下:
- 用户向服务器发送用户名和密码
- 服务器验证通过后,在当前对话(session)中保存相关数据,比如用户名、登入时间等
- 服务器向用户返回一个 session_id ,写入用户的 cookie 。
- 用户以后的没一次请求都会通过 Cookie,将session_id 传回服务器。
- 服务器收到 session_id后,到保存的session数据中找到对应的用户,由此得知用户的身份。
这种模式看起来还不错的,但是扩展性不好。单机没问题,如果是服务器集群,或者是跨域的服务导向架构,就要求 session 数据共享,每台服务器都能够读取 session 。
比如,A网站和B网站是同一家公司的关联服务。现在要求,用户只要登入了其中一个网站再访问另一个网站时不需要重新登入直接进入,怎么实现呢?
我们可以将 session 数据持久化,写入数据库或者特别的持久层。各个服务收到请求后,都向持久层请求数据。这个方案的有点是架构清晰,缺点是工程量较大。另外,持久层万一挂了,就会单点失败,依赖性强。
另一种方法才是我们今天的重点,我们使服务器索性不保存session数据,所有数据都保存在客户端,每次请求都发回服务器。JWT 就是这种方案的一个代表。
JWT 原理
JWT 的原理是,服务器认证后,生成一个json 对象发回给用户类似如下:
1 | { |
在以后用户和服务器通信的时候,都会发送这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候会加上一个签名。
JWT 的数据结构
1 | 形式:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.(这里有一个点) |
第一个点前为header,第二个点前为payload,第二个点后为signture。
header 部分
header部分是一个JSON 对象,描述 JWT 的元素,通常是这样的:(最初形式)
1 | { |
其中,alg
属性的值表示签名的算法,默认是HMAC SHA256 (写成 HS256)。typ
属性表示这个令牌的类型,JWT 令牌统一写成 JWT
。 然后进行base64URl。
payload 部分
payload 部分也是一个 JSON 对象,用来存放实际要传递的数据。JWT 规定了 7 个官方字段:
1 | iss (issuer):签发人 |
但然也可以自定义私有字段。
注意 : JWT 默认是不加密的,任何人都可以读到,所以不要吧秘密信息放在这部分。(但也可以加密)然后进行base64URl。
signature 部分
signature 部分是对前两个部分的签名,防止数据篡改。
首先需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄漏给用户。然后使用 Header 里面的指定签名算法(默认Hs256)按照下面公式进行签名:
1 | HMACSHA256( |
计算出签名以后把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用”点”(.)
分隔,就可以返回给用户。
Base64URL
前面说过,Header和payload需要进行串行话的算法是 Base64URL。这个算法和base64基本类似。区别在于,JWT作为一个令牌,有些场合可能放到URL中,Base64中有+
、 /
、 =
三个字符,在base64URL中,=
会被省略掉、+
会被替换为 -
、/
替换为 _
。
JWT 的使用方式
客户端收到服务器返回的 JWT 可以存储在 Cookie 里,也可以存储在 localStorage。
此后,客户端每次与服务器通信时都会带上JWT。可以把它放到cookie中自动发送,但是这样不可以跨域,所以更好的做法是将其放到 头部 Authorization
字段。 另外也可以跨域的时候,JWT就放在 POST请求的数据体里面。
JWT 的几个特点
- 默认不加密,但也可以加密,生成原始Token后可以使用密钥再进行一次加密。
- 不加密的情况下,不能将秘密数据写入 JWT。
- JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。
- JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。
- JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。
- 为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。