# 什么是JWT令牌?

在解决这个问题之前,我们先了解为什么需要JWT令牌

# 登录校验

登录校验,指的是我们在服务器端接收到浏览器发送过来的请求之后,首先我们要对请求进行校验。先要校验一下用户登录了没有,如果用户已经登录了,就直接执行对应的业务操作就可以了;如果用户没有登录,此时就不允许他执行相关的业务操作,直接给前端响应一个错误的结果,最终跳转到登录页面,要求他登录成功之后,再来访问对应的数据。

# 无状态的HTTP协议

HTTP协议是无状态协议,所谓无状态,指的是每一次请求都是独立的,下一次请求并不会携带上一次请求的数据。因为HTTP协议是无状态的,两次请求之间是独立的,所以是无法判断这个员工到底登陆了没有。

# 无状态HTTP协议实现登录校验

  1. 在用户登录成功后,需要将用户登录成功的信息存起来,记录用户已经登录成功的标记。
  2. 在浏览器发起请求时,需要在服务端进行统一拦截,拦截后进行登录校验。

我们要完成以上操作,会涉及到web开发中的两个技术:

  1. 会话技术:用户登录成功之后,在后续的每一次请求中,都可以获取到该标记。
  2. 统一拦截技术:过滤器Filter、拦截器Interceptor

# 会话技术

在web开发当中,会话指的就是浏览器与服务器之间的一次连接,我们就称为一次会话。知道了会话的概念了,接下来我们再来了解下会话跟踪。

# 会话跟踪

一种维护浏览器状态的方法,服务器需要识别多次请求是否来自同一浏览器,以便在同一次会话的多次请求间共享数据。会话跟踪技术有三种:

  1. Cookie(客户端会话跟踪技术):数据存储在客户端浏览器当中
  2. Session(服务端会话跟踪技术):数据存储在储在服务端
  3. 令牌技术

tips: 可以点击F12,打开浏览器的开发者工具,在应用选项卡中查看全部COOKIE的信息。

为了解决这个问题,传统的做法是使用COOKIE和SESSION。

COOKIE是存储在客户端浏览器中的小文件,后端可以通过设置HTTP响应头中的Set-Cookie字段来让浏览器保存COOKIE。浏览器在后续的请求中会自动携带这些COOKIE,服务器可以通过读取COOKIE来识别用户。

优点:

  • HTTP协议中支持的技术(像Set-Cookie 响应头的解析以及 Cookie 请求头数据的携带,都是浏览器自动进行的,是无需我们手动操作的)

缺点:

  1. 移动端APP(Android、IOS)中无法使用Cookie
  2. 不安全,用户可以自己禁用Cookie
  3. Cookie不能跨域

# SESSION

当用户登录后,服务器会在内存或者数据库中保存用户的登录状态,并生成一个唯一的SESSION ID,发送给客户端。客户端在后续的请求中会携带这个SESSION ID,服务器通过这个ID来识别用户的登录状态。

优点:

  • Session是存储在服务端的,安全

缺点:

  1. 服务器集群环境下无法直接使用Session
  2. 移动端APP(Android、IOS)中无法使用Cookie
  3. 用户可以自己禁用Cookie
  4. Cookie不能跨域

# 关于JWT令牌

tips:与cookie不一样,令牌一般是存储在浏览器的本地存储(Local Storage)或者会话存储(Session Storage)中。 (可以点击F12,打开浏览器的开发者工具,在应用选项卡中存储那一块找到)

JWT(JSON Web Token)是一种用于在网络应用环境中传递声明的紧凑且自包含的方式。它通常用于身份验证和信息交换。JWT由三部分组成(用两个.分割开):header.payload.signature

# 头部(Header)

{
  "alg": "HS256",
  "typ": "JWT"
}
  • alg:签名算法,比如 HS256、RS256
  • typ:类型(固定是 JWT)

然后它会被 Base64URL 编码 得到一段字符串。

# 载荷(Payload)

存放声明(claims)

{
  "userId": 123,
  "username": "bob",
  "exp": 1699999999
}
  • 用户信息
  • 过期时间(exp)
  • 权限、角色等

然后它会被 Base64URL 编码 得到一段字符串。

⚠ Payload 是公开的!任何人都能解码!(Base64 是可逆编码,不是加密!)所以请不要存密码什么的

# 签名(Signature)

最关键的部分是 签名(signature),它保证 Token 没被篡改。 签名生成方式(HS256 为例):

signature = HMACSHA256(
    base64UrlEncode(header) + "." + base64UrlEncode(payload), 
    secret_key
)

关键部分是用secret_key和加密算法(如:HS256)加密,用相同的密钥和算法解密校验,secret_key是服务器端的密钥,只有服务器知道