Cookie、Session和JWT

会话

从客户端与服务器建立HTTP连接到断开连接的过程,称为一个会话。一个会话可以包含多次请求与响应。

由于HTTP协议是无状态的,服务器无法区分前后两个请求是否来自同一设备,因此有了三种会话跟踪方案:CookieSessionJWT

Cookie

存储在浏览器中,发起请求时浏览器会自动带上Cookie,收到响应时浏览器会自动存储Cookie,是HTTP协议自带的功能。

Cookie的格式如下:

1
2
3
4
5
// 服务器响应
Set-Cookie: Token=xxxxxx; Domain=.saoke.com; Path=/; HttpOnly; Secure; SameSite=Lax

// 浏览器发送
Cookie: Token=xxxxxx; Domain=.saoke.com; Path=/; HttpOnly; Secure; SameSite=Lax

Cookie的缺点:

  1. 只有浏览器支持Cookie,APP不支持
  2. 不安全,存在CSRF攻击,且用户可以禁用Cookie
  3. 不能跨域

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Slf4j
@RestController
public class CookieController {

@GetMapping("/cookie1")
public Result cookie1(HttpServletResponse response) {
response.addCookie(new Cookie("key", "value"));
return Result.ok();
}

@GetMapping("/cookie2")
public Result cookie2(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
if (cookie.getName().equals("key")) {
log.debug(cookie.getValue());
}
}
return Result.ok();
}
}

Session

Session是服务器在每个HTTP连接中对应的会话对象。Session存储在服务器中,其底层是基于Cookie实现的。

在建立连接第一次请求时,服务器会创建一个Session,每一次请求服务器都可以获取到其Session。每个Session有一个id,服务器响应数据时,会将Session的id放在Cookie中响应给浏览器。浏览器每次请求时会在Cookie中带上Session的id,服务器再根据id找到对应的Session,从而分辨前后两次请求是否是同一会话。

1
Set-Cookie: sessionId=xxx

Session的优点:

  1. 存储在服务端,安全

缺点:

  1. 在服务器集群环境下无法直接使用,每次请求可能给到不同服务器处理,不同服务器保存的Session不互通
  2. Cookie的缺点它也有

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Slf4j
@RestController
public class SessionController {

@GetMapping("/session1")
public Result session1(HttpSession session) {
session.setAttribute("key", "value");
return Result.ok();
}

@GetMapping("/session2")
public Result session2(HttpServletRequest request) {
HttpSession session = request.getSession();
log.info(session.getAttribute("key").toString());
return Result.ok();
}
}

JWT

全称JSON Web Token。格式:

eyJhbGciOiJIUzI1NiIsIn.eyJ1c2VySWQiOjEyMywiZX.dBjftJeZ4CVP-mB92K27uh

其由两个.分割为三部分。

Header 头部

绿色的第一部分为Header(头部),包含加密算法、Token类型等信息,如:

1
2
3
4
{
"alg": "HS256",
"typ": "JWT"
}

然后将该JSON字符串进行Base64编码,得到的结果即为绿色的第一部分。

Payload 负载

紫色的第二部分为Payload(负载),存储实际传递的数据。

如:

1
2
3
4
5
{
"userId": 123456,
"username": "骚客",
"exp": 1700000000
}

同样进行Base64编码,得到的结果即为黄色的第二部分。

这里要注意,JWT只是将数据进行Base64编码,并没有加密,因此不宜传输敏感数据。

Signature 签名

红色的第三部分为Signature(签名),用于校验前两部分是否被修改。

将前两部分Header和Payload用Header中指定的加密算法进行加密,加密后的密文再进行Base64编码,即为红色的第三部分。

这里加密用的密钥是保存在服务端的,即便攻击者要篡改JWT,由于不知道密钥,因此无法计算出对应的签名。

优缺点

JWT的优点:

  • 支持PC端、移动端
  • 解决集群环境下的认证问题
  • 减轻服务器存储压力

缺点:

  • 需要自己实现