JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案。
- JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。
- JWT 不加密的情况下,不能将秘密数据写入 JWT。
- JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数
- JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。
- JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。
- 为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。
1. token 认证
当用户成功登陆系统并成功验证有效之后,服务器会利用某种机制产生一个token字符串,这个token中可以包含很多信息,例如来源IP,过期时间,用户信息等, 把这个字符串下发给客户端,客户端在之后的每次请求中都携带着这个token,携带方式其实很自由,无论是cookie方式还是其他方式都可以。当服务端收到请求,取出token进行验证(可以验证来源ip,过期时间等信息),如果合法则允许进行操作。
1.1 优点
- 支持跨域访问:Cookie是不允许垮域访问的,这一点对Token机制是不存在的,前提是传输的用户认证信息通过HTTP头传输。
- 无状态: Token机制在服务端不需要存储session信息,因为Token自身包含了所有登录用户的信息,只需要在客户端的cookie或本地介质存储状态信息。
- 解耦:不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在你的API被调用的时候,你可以进行Token生成调用即可.
- 适用性更广:只要是支持http协议的客户端,就可以使用token认证。
- 服务端只需要验证token的安全,不必再去获取登录用户信息,因为用户的登录信息已经在token信息中。
- 基于标准化:你的API可以采用标准化的 JSON Web Token (JWT).
1.2 缺点
- 网络传输的数据量增大:由于token中存储了大量的用户和安全相关的信息,所以比单纯的cookie信息(例如session_id)要大很多,传输过程中需要消耗更多流量,占用更多带宽,
- 和所有的客户端认证方式一样,如果想要在服务端控制token的注销有难度,而且也很难解决客户端的劫持问题。
- 由于token信息在服务端增加了一次验证数据完整性的操作,所以比session的认证方式增加了cpu的开销。
但是整体来看,基于token的认证方式还是比session和cookie方式要有很大优势。在所知的token认证中,jwt是一种优秀的解决方案。
2. jwt 结构
JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。
一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。中间用点(.
)分隔成三个部分。注意JWT 内部是没有换行的。
2.1 头部
header典型的由两部分组成:token的类型(“JWT”)和算法名称(比如:HMAC SHA256或者RSA等等)。
1 | { |
将上面的内容进行base64编码,可以得到我们JWT的头部,编码后如下:
1 | ewogICJhbGciOiAiSFMyNTYiLAogICJ0eXAiOiAiSldUIgp9ICA= |
2.2 Payload
Payload 部分也是一个JSON对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用。
1 | iss (issuer):该JWT的签发者 |
除了以上字段之外,你完全可以添加自己想要的任何字段,这里还是提醒一下,由于jwt的标准,信息是不加密的,所以一些敏感信息最好不要添加到json里面
1 | { |
现在我们需要将负载这整个部分进行base64编码,编码后结果如下:
1 | ewogICAgImlzcyI6ICJMZWZ0by5jb20iLAogICAgImlhdCI6IDE1MDAyMTgwNzcsCiAgICAiZXhwIjogMTUwMDIxODA3NywKICAgICJhdWQiOiAid3d3LmxlZnRzby5jb20iLAogICAgInN1YiI6ICJsZWZ0c29AcXEuY29tIiwKICAgICJ1c2VyX2lkIjogImRjMmM0ZWVmZTJkMTQxNDkwYjZjYTYxMmUyNTJmOTJlIiwKICAgICJ1c2VyX3Rva2VuIjogIjA5ZjdmMjVjZGIwMDM2OTljZWUwNTc1OWU3OTM0ZmIyIgp9 |
2.3 Signature
为了得到签名部分,你必须有编码过的header、编码过的payload、一个秘钥(这个秘钥只有服务端知道),签名算法是header中指定的那个,然对它们签名即可。
1 | HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) |
首先需要将头部和负载通过.链接起来就像这样:header.Payload,上述的例子链接起来之后就是这样的:
1 | ewogICJhbGciOiAiSFMyNTYiLAogICJ0eXAiOiAiSldUIgp9ICA=.ewogICAgImlzcyI6ICJMZWZ0by5jb20iLAogICAgImlhdCI6IDE1MDAyMTgwNzcsCiAgICAiZXhwIjogMTUwMDIxODA3NywKICAgICJhdWQiOiAid3d3LmxlZnRzby5jb20iLAogICAgInN1YiI6ICJsZWZ0c29AcXEuY29tIiwKICAgICJ1c2VyX2lkIjogImRjMmM0ZWVmZTJkMTQxNDkwYjZjYTYxMmUyNTJmOTJlIiwKICAgICJ1c2VyX3Rva2VuIjogIjA5ZjdmMjVjZGIwMDM2OTljZWUwNTc1OWU3OTM0ZmIyIgp9 |
由于HMacSHA256加密算法需要一个key,我们这里key暂时用liuvv吧
加密后的内容为:
1 | 686855c578362e762248f22e2cc1213dc7a6aff8ebda52247780eb6b5ae91877 |
对上面的签名内容进行base64编码得到最终的签名
1 | Njg2ODU1YzU3ODM2MmU3NjIyNDhmMjJlMmNjMTIxM2RjN2E2YWZmOGViZGE1MjI0Nzc4MGViNmI1YWU5MTg3Nw== |
最终的JWT:
1 | ewogICJhbGciOiAiSFMyNTYiLAogICJ0eXAiOiAiSldUIgp9ICA=.ewogICAgImlzcyI6ICJMZWZ0by5jb20iLAogICAgImlhdCI6IDE1MDAyMTgwNzcsCiAgICAiZXhwIjogMTUwMDIxODA3NywKICAgICJhdWQiOiAid3d3LmxlZnRzby5jb20iLAogICAgInN1YiI6ICJsZWZ0c29AcXEuY29tIiwKICAgICJ1c2VyX2lkIjogImRjMmM0ZWVmZTJkMTQxNDkwYjZjYTYxMmUyNTJmOTJlIiwKICAgICJ1c2VyX3Rva2VuIjogIjA5ZjdmMjVjZGIwMDM2OTljZWUwNTc1OWU3OTM0ZmIyIgp9.Njg2ODU1YzU3ODM2MmU3NjIyNDhmMjJlMmNjMTIxM2RjN2E2YWZmOGViZGE1MjI0Nzc4MGViNmI1YWU5MTg3Nw== |
可以在线玩玩:https://www.bejson.com/jwt/
3. jwt 使用
3.1 使用流程
客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。此后,客户端每次与服务器通信,都要带上这个 JWT。
你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面。
1 | Authorization: Bearer <token> |
3.2 JWT 可以被”解码”(不是解密)
JWT 的结构是:header.payload.signature
,前两部分(header 和 payload)只是 Base64 编码,任何人都可以解码查看内容。
1 | // JWT 示例 |
所以 JWT 不适合存储敏感信息。
3.3 JWT 默认是不加密的
JWT 的内容是 公开可见的,只是 Base64 编码(可逆的)。
你可以尝试伪造 JWT,但是服务器会通过签名验证发现伪造!这就是 JWT 安全机制的核心。
1 | // 服务器端生成 JWT |
你可以伪造 JWT 的 header 和 payload,但无法伪造有效的签名,服务器通过重新计算签名来验证 JWT 的真实性。
3.4 jwt 泄露了,会有问题吗
JWT 是无状态的,服务器无法主动撤销, 一旦签发,在过期前都是有效的。即使用户修改密码,旧 JWT 仍然有效。
- 缩短 JWT 有效期
- 实现 Token 黑名单机制
- 使用 redis 存储 jwt,修改密码后删除。