会话跟踪令牌校验

Spring Boot cookie 会话

Created: 9/1/2025 15:40
Updated: 9/1/2025 23:48
Exported: 9/1/2025 23:49
Link: https://chatgpt.com/c/68b54dc1-1c64-8332-9449-51924b63d2bd

Prompt:cookie会话跟踪方案的原理

Response:

好的,我来详细讲解一下 Cookie 会话跟踪方案的原理


1. 背景问题

HTTP 协议本身是 无状态 的:

  • 服务器无法知道两次请求是否来自同一个用户。
  • 比如用户登录成功后,如果没有额外机制,下一次请求服务器并不知道这个用户是谁。

所以,需要一种机制来 维持会话状态,这就是 Session Tracking(会话跟踪)


Cookie 是存储在客户端浏览器的一小段文本信息,由服务器生成并通过 Set-Cookie 响应头发送给浏览器。

  • 浏览器保存下来后,在后续请求中会自动携带该 Cookie。
  • 这样服务器就能识别请求是否来自同一个客户端,从而维持会话。

  1. 用户访问网站
    • 客户端第一次访问服务器时,还没有 Cookie。
  2. 服务器生成会话标识(Session ID)
    • 服务器为该用户创建一个唯一的 Session(存储在服务器内存或数据库中)。
    • 生成一个随机字符串(Session ID)。
  3. 通过响应头返回 Cookie
    • 服务器在响应头中设置:
      1
      Set-Cookie: JSESSIONID=abc123; Path=/; HttpOnly
    • 浏览器接收到后,会保存 JSESSIONID=abc123
  4. 浏览器后续请求携带 Cookie
    • 以后浏览器访问同一域名时,请求头会自动附加:
      1
      Cookie: JSESSIONID=abc123
    • 服务器根据 Cookie 中的 Session ID,找到对应的会话数据。
  5. 服务器识别用户并维持状态
    • 服务器就能知道该请求属于哪个已登录用户,从而实现会话跟踪。

  • Session ID 存在 Cookie 中,而不是存储敏感数据(避免泄露风险)。
  • 浏览器自动携带 Cookie,避免开发者手动传递参数。
  • 通常结合 Session 机制
    • Cookie 存储会话 ID
    • Session 存储用户数据(登录状态、购物车等)

优点

  • 实现简单,浏览器自动支持。
  • 可以跨多个请求维持会话状态。

缺点

  • 依赖客户端是否支持 Cookie(有些用户禁用 Cookie)。
  • 存在安全风险(例如被窃取可能导致会话劫持)。
  • 不能跨域使用(受同源策略约束)。

🔑 总结一句话
Cookie 会话跟踪的核心原理是:服务器通过在客户端保存一个唯一标识(Session ID)的 Cookie,并在后续请求中依靠该 Cookie 来识别用户,从而实现状态保持。

Prompt:session会话跟踪方案的原理

Response:

好的 👍,我来讲一下 Session 会话跟踪方案的原理,它和 Cookie 有关联,但比 Cookie 更安全和常用。


1. 背景

HTTP 协议是 无状态 的,每次请求之间服务器并不知道客户端的身份。

  • Cookie 会话跟踪:通过在客户端存储会话标识实现。
  • Session 会话跟踪:通过在服务器端存储用户数据,只在客户端保存一个 Session ID

2. Session 会话跟踪方案的原理

步骤流程

  1. 用户第一次访问服务器
    • 客户端发起请求,服务器为其创建一个新的 Session 对象(存储在服务器端内存/数据库/分布式缓存中)。
    • 这个 Session 中可以存放该用户的会话信息,比如登录状态、用户名、购物车等。
  2. 生成 Session ID 并返回客户端
    • 服务器为该 Session 生成一个唯一的 Session ID(如 xyz123)。
    • 通过响应头 Set-Cookie 发送给客户端:
      1
      Set-Cookie: JSESSIONID=xyz123; Path=/; HttpOnly
  3. 客户端保存 Session ID
    • 浏览器保存这个 Cookie(里面只有一个 Session ID,不包含具体的会话数据)。
  4. 客户端再次请求时携带 Session ID
    • 后续请求时,浏览器会自动在请求头中附加:
      1
      Cookie: JSESSIONID=xyz123
  5. 服务器查找对应的 Session 数据
    • 服务器收到请求后,根据 JSESSIONID 找到对应的 Session 对象,从而恢复该用户的会话信息。

3. 关键点

  • 客户端只保存 Session ID,具体的会话数据存在服务器。
  • Session 生命周期:默认存活一定时间(如 30 分钟),若用户持续操作会刷新时间。
  • Session 存储方式
    • 单机内存(简单但不适合集群)
    • 数据库
    • Redis、Memcached 等分布式缓存

特性 Cookie 跟踪 Session 跟踪
数据存储位置 客户端 服务器端
安全性 较低(容易被篡改) 较高(客户端只存 Session ID)
占用资源 不占用服务器资源 占用服务器内存/存储
适用场景 简单信息存储(语言偏好等) 需要存储用户敏感信息、登录状态

5. 简单总结

Session 会话跟踪的核心原理是:
👉 服务器端为每个用户创建 Session 存储会话数据,同时给客户端一个唯一的 Session ID(通常存在 Cookie 中)。客户端每次请求时携带 Session ID,服务器通过该 ID 找回对应的会话数据,从而实现会话跟踪。

Prompt:session会话跟踪方案优缺点

Response:

好的 ✅,下面我来总结一下 Session 会话跟踪方案的优缺点


一、优点

  1. 安全性高
    • 客户端只保存 Session ID,实际的用户数据保存在服务器端,不容易被篡改或窃取。
    • 比 Cookie 存储用户数据更安全,尤其适合保存敏感信息(登录状态、权限等)。
  2. 支持存储复杂数据
    • Session 可以在服务器端保存对象、集合等复杂数据,而 Cookie 只能保存少量字符串。
  3. 透明性
    • 对开发者和用户相对透明,浏览器只需要保存并发送 Session ID,具体数据交互由服务器完成。
  4. 方便实现权限控制和个性化服务
    • 由于会话数据在服务器端集中管理,方便实现登录校验、购物车、个性化推荐等功能。

二、缺点

  1. 服务器资源占用大
    • 每个活跃用户都会占用服务器内存或存储空间。用户量大时,服务器负担较重。
  2. 扩展性差(单机限制)
    • 如果 Session 存储在单台服务器内存中,那么在集群或分布式环境下,可能导致 负载均衡时会话不一致
    • 一般需要使用 Redis、数据库等集中式存储来解决。
  3. 依赖 Cookie 或 URL 重写
    • Session ID 一般通过 Cookie 传递,如果用户禁用 Cookie,就需要 URL 重写(在 URL 中附加 ;JSESSIONID=xxx),增加复杂性和安全风险。
  4. Session 过期机制
    • Session 默认会过期(如 30 分钟),过期后用户需要重新登录。
    • 过期时间不好控制,过长会增加服务器压力,过短影响用户体验。

三、总结

Session 会话跟踪

  • 优点:安全、灵活、能存储复杂数据,适合保存敏感信息。
  • 缺点:占用服务器资源,不利于大规模分布式扩展,需要额外机制解决负载均衡和高并发问题。

Prompt:令牌会话跟踪方案优缺点

Response:

好的 👍,我来总结一下 令牌(Token)会话跟踪方案优缺点

这里的 Token 一般指基于 JWT(JSON Web Token) 或类似机制的无状态会话方案。


一、令牌会话跟踪方案的原理简述

  • 用户登录成功后,服务器生成一个 Token(通常是加密签名的字符串,包含用户身份信息和过期时间等)。
  • 服务器把 Token 返回给客户端(通常保存在 浏览器的 LocalStorage 或 Cookie 中)。
  • 客户端后续请求时在 Authorization 头中携带 Token:
    1
    Authorization: Bearer <token>
  • 服务器通过验证 Token 的签名和有效期来识别用户身份,而 无需在服务器端保存会话状态

二、优点

  1. 无状态,易于扩展
    • 服务器不需要存储会话数据,只需验证 Token,有利于分布式和微服务架构。
    • 多台服务器之间不需要共享 Session。
  2. 跨域支持好
    • Token 可以放在请求头中(而不是 Cookie),天然支持跨域请求,适合前后端分离架构。
  3. 性能较高
    • 服务器无需频繁查找 Session 数据,只要校验 Token 即可。
    • 减轻了服务器内存/存储的压力。
  4. 可扩展性强
    • Token 中可以携带一些必要的用户信息(如角色、权限),服务器可直接使用。
  5. 支持移动端 / API 调用
    • 不依赖浏览器 Cookie,在移动端、第三方应用中更通用。

三、缺点

  1. 安全风险
    • 如果 Token 被窃取(如 XSS、网络泄露),攻击者可以伪装用户,直到 Token 过期。
    • 无法像 Session 那样在服务器端立即销毁某个 Token(除非额外维护黑名单)。
  2. 体积较大
    • JWT 等 Token 一般包含用户信息和签名,体积比 Session ID 大,每次请求都会增加带宽开销。
  3. 不可控的过期问题
    • 一旦 Token 签发,除非有黑名单机制,服务器无法单方面让其失效(例如用户主动登出时)。
  4. 实现复杂度高
    • 需要设计 Token 签发、刷新、过期、黑名单等机制,比 Session 更复杂。
  5. 信息泄露风险
    • 如果把过多敏感信息(如邮箱、角色等)写入 Token,即使签名防篡改,但内容仍然可能被解码(Base64 可逆),需要注意加密敏感字段。

四、总结对比

特性 Session 会话 Token 会话
存储位置 服务器端(Session 数据) 客户端(Token)
服务器压力 占用内存,压力大 无状态,压力小
扩展性 集群需共享 Session 天然支持分布式
安全性 较高,可立即销毁 Session Token 被窃取风险大,销毁困难
跨域支持 依赖 Cookie(受同源限制) 支持跨域(请求头传递)
适用场景 传统网站,单体应用 前后端分离,移动端,微服务,API 调用

👉 结论:

  • Session 更安全,适合传统单体 Web 应用。
  • Token 更灵活、可扩展,适合前后端分离、微服务和跨平台应用。

Prompt:jwt令牌怎么使用

Response:

好的 👍,我来详细讲一下 JWT(JSON Web Token)令牌的使用方法


1. JWT 的组成

JWT 本质是一个字符串,分为三部分,用 . 分隔:

1
header.payload.signature
  • Header(头部):说明算法和类型,例如:
    1
    { "alg": "HS256", "typ": "JWT" }
  • Payload(载荷):存放用户信息、过期时间等,例如:
    1
    { "userId": 123, "role": "admin", "exp": 1690999999 }
  • Signature(签名):由 header + payload + secret 计算出来,防止数据被篡改。

最终是一个 Base64 编码后的长字符串,比如:

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

2. 使用流程

(1)用户登录

  • 用户提交用户名和密码到服务器。
  • 服务器验证成功后,生成一个 JWT
    • 设置用户信息(userId、角色)
    • 设置过期时间(如 2 小时)
    • 用密钥(secret)签名
  • 服务器返回 JWT 给客户端。

(2)客户端存储 JWT

  • 客户端保存 JWT(常见存储位置):
    • LocalStorage:适合单页应用(SPA)
    • Cookie:适合传统 Web 应用

(3)携带 JWT 访问接口

  • 客户端在每次请求时,将 JWT 放在 HTTP 请求头里:
    1
    Authorization: Bearer <jwt-token>

(4)服务器验证 JWT

  • 服务器拿到 Token 后:
    1. 检查签名是否正确(防篡改)
    2. 检查是否过期
    3. 解析 payload,获取用户信息

(5)服务器返回数据

  • 如果验证通过,就返回对应的资源或数据。
  • 如果失败(过期、伪造),返回 401 Unauthorized。

3. 使用示例(伪代码)

服务端(Java Spring Boot)

1
2
3
4
5
6
7
8
9
10
// 登录成功后生成 JWT
String jwt = Jwts.builder()
.setSubject("user123")
.claim("role", "admin")
.setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000)) // 1小时过期
.signWith(SignatureAlgorithm.HS256, "mySecretKey")
.compact();

// 返回给客户端
return ResponseEntity.ok(Map.of("token", jwt));

客户端(JavaScript)

1
2
3
4
5
6
7
8
9
// 保存 JWT
localStorage.setItem("token", response.token);

// 每次请求时带上 JWT
fetch("/api/data", {
headers: {
"Authorization": "Bearer " + localStorage.getItem("token")
}
});

服务端验证

1
2
3
4
5
6
7
8
String token = request.getHeader("Authorization").replace("Bearer ", "");
Claims claims = Jwts.parser()
.setSigningKey("mySecretKey")
.parseClaimsJws(token)
.getBody();

String userId = claims.getSubject(); // 获取 userId
String role = claims.get("role", String.class);

4. JWT 的适用场景

  • 前后端分离:前端 Vue/React + 后端 Spring Boot/Node.js
  • 微服务架构:不同服务间通过 JWT 验证用户身份
  • 移动端 API 认证:APP 与服务端通信

✅ 总结:
JWT 的使用流程是:登录生成 Token → 客户端保存 → 每次请求带上 Token → 服务端验证签名与有效期 → 授权访问资源。

Prompt:filter的开发步骤及注意事项

Response:

你问的是 Filter(过滤器) 的开发步骤及注意事项,我结合 Java Web(Servlet Filter)的场景给你整理:


一、Filter 的开发步骤

1. 创建 Filter 类

  • 必须实现 javax.servlet.Filter 接口:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    import javax.servlet.*;
    import java.io.IOException;
    public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    // 初始化逻辑(可选)
    System.out.println("Filter 初始化");
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    throws IOException, ServletException {
    // 1. 前置处理
    System.out.println("请求到达 Filter,执行前置逻辑");
    // 2. 放行请求(否则后续资源无法访问)
    chain.doFilter(request, response);
    // 3. 后置处理
    System.out.println("请求返回 Filter,执行后置逻辑");
    }
    @Override
    public void destroy() {
    // 释放资源(可选)
    System.out.println("Filter 销毁");
    }
    }

2. 注册 Filter

有两种方式:

(1)在 web.xml 配置

1
2
3
4
5
6
7
8
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>com.example.filter.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

(2)使用注解(推荐)

1
2
3
4
5
6
import javax.servlet.annotation.WebFilter;

@WebFilter("/*") // 拦截所有请求
public class MyFilter implements Filter {
...
}

3. 部署运行

  • 启动应用服务器(Tomcat/Jetty),Filter 会随应用启动加载。
  • 所有匹配 url-pattern 的请求都会经过该 Filter。

二、常见用途

  • 登录校验(未登录用户拦截重定向到登录页)
  • 权限控制(判断用户角色是否能访问资源)
  • 请求/响应编码处理(统一设置 UTF-8)
  • 日志记录(记录请求耗时、访问路径等)
  • 跨域处理(设置 CORS 响应头)

三、开发注意事项

  1. 必须调用 chain.doFilter()
    • 否则请求不会继续传递,导致资源无法访问。
    • 如果需要拦截(如未登录),可以不调用,而是直接返回响应。
  2. Filter 链顺序
    • 多个 Filter 按配置顺序执行(先进入的最后返回)。
    • 在 Spring Boot 里,可以用 @Order 或者 FilterRegistrationBean 控制顺序。
  3. 避免重复读取请求流
    • HttpServletRequest.getInputStream() / getReader() 只能读取一次,若 Filter 里读取了,需要缓存,否则 Controller 里拿不到。
  4. 线程安全
    • Filter 实例是单例的,不要在成员变量中存放请求相关数据,否则可能出现并发问题。
  5. 性能问题
    • 尽量保持逻辑简洁,不要在 Filter 中写耗时操作(比如数据库查询)。
  6. 异常处理
    • 在 Filter 中捕获并统一处理异常,避免直接抛出导致服务器 500。
  7. 跨域时注意 OPTIONS 请求
    • 如果做 CORS 处理,要特别放行 OPTIONS 预检请求。

总结
Filter 的开发步骤就是 实现接口 → 配置映射 → 处理请求 → 放行或拦截
注意 调用链、线程安全、异常处理、性能优化,这是写 Filter 时的关键点。

Prompt:什么是过滤器链

Response:

好问题 👍,我来给你解释 过滤器链(Filter Chain)


一、什么是过滤器链

在 Java Web 中,可以配置多个过滤器(Filter),请求到达服务器后,会依次通过这些过滤器,形成一个 链式调用结构,这就叫 过滤器链(Filter Chain)

它的特点是:

  • 按顺序依次执行:第一个 Filter → 第二个 Filter → … → Servlet。
  • 先进入的最后返回:请求正向经过过滤器链,响应再逆向返回。
  • 每个 Filter 都可以选择:
    1. 调用 chain.doFilter(request, response) 放行(交给下一个 Filter 或目标 Servlet);
    2. 不调用 chain.doFilter(),直接拦截请求(例如未登录跳转登录页)。

二、执行流程示例

假设配置了 2 个过滤器:

  • AuthFilter(登录校验)
  • LogFilter(日志记录)

执行流程如下:

  1. 请求到达 → AuthFilter.doFilter() 执行前置逻辑
  2. 调用 chain.doFilter() → 请求进入 LogFilter.doFilter() 执行前置逻辑
  3. 调用 chain.doFilter() → 请求进入目标 Servlet(比如 Controller)
  4. Servlet 执行完毕,响应返回 → 回到 LogFilter.doFilter() 执行后置逻辑
  5. 再回到 AuthFilter.doFilter() 执行后置逻辑
  6. 最终响应返回客户端

所以调用栈类似:

1
2
3
4
5
请求进入:
AuthFilter → LogFilter → Servlet

响应返回:
Servlet → LogFilter → AuthFilter

三、过滤器链的用途

  • 请求预处理:统一编码、日志记录、跨域设置、参数校验
  • 安全控制:登录校验、权限验证、防止 SQL 注入、XSS 过滤
  • 响应后处理:数据格式化、响应压缩、日志收集

四、注意事项

  1. Filter 执行顺序很重要
    • web.xml 中,Filter 的配置顺序就是执行顺序。
    • 在 Spring Boot 中,可以通过 @Order 注解或 FilterRegistrationBean.setOrder() 控制。
  2. 链式调用必须小心
    • 如果某个 Filter 忘记调用 chain.doFilter(),后续过滤器和目标 Servlet 就不会执行。
    • 如果拦截请求(比如未登录),必须明确返回响应。
  3. Filter 与 Interceptor 的区别
    • Filter 在 Servlet 容器层面起作用,能拦截所有请求(包括静态资源)。
    • Interceptor 在 Spring MVC 层面起作用,只能拦截 Controller 请求。

✅ 总结:
过滤器链就是多个 Filter 串联起来的执行过程,请求和响应都会依次经过这些 Filter,就像一层层的管道一样,可以灵活地进行拦截、处理和放行。

Prompt:拦截器的开发步骤及注意事项

Response:

好问题 👍,我来总结一下 拦截器(Interceptor)的开发步骤及注意事项,以 Spring MVC / Spring Boot 的拦截器为例。


一、拦截器开发步骤

1. 创建拦截器类

  • 实现 HandlerInterceptor 接口,或者继承 HandlerInterceptorAdapter(旧方式,不推荐)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyInterceptor implements HandlerInterceptor {

// 1. 请求到达 Controller 之前执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("拦截器 preHandle: 请求进入");
// 返回 true 才会继续执行,false 会被拦截
return true;
}

// 2. Controller 执行完毕,视图渲染之前执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, org.springframework.web.servlet.ModelAndView modelAndView)
throws Exception {
System.out.println("拦截器 postHandle: Controller 执行后");
}

// 3. 整个请求结束后执行(类似 finally)
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
System.out.println("拦截器 afterCompletion: 请求完成");
}
}

2. 注册拦截器

Spring Boot(推荐方式)

在配置类中实现 WebMvcConfigurer 接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/**") // 拦截所有请求
.excludePathPatterns("/login", "/css/**", "/js/**"); // 放行登录、静态资源
}
}

二、拦截器常见用途

  • 登录校验(未登录用户禁止访问某些接口)
  • 权限控制(判断是否有权限访问资源)
  • 接口调用日志(记录请求参数、响应时间)
  • 数据预处理(请求参数格式化、国际化信息设置)

三、开发注意事项

  1. 拦截器顺序
    • 多个拦截器时,执行顺序与注册顺序一致。
    • 执行流程:
      • preHandle():按顺序执行
      • postHandle() & afterCompletion():按逆序执行
  2. 返回值控制
    • preHandle() 返回 true:继续执行后续拦截器或 Controller
    • preHandle() 返回 false:请求被拦截,可以直接写响应内容(如返回 JSON 错误信息),不会进入 Controller
  3. 异常处理
    • 如果 Controller 抛异常,postHandle() 不会执行,但 afterCompletion() 一定会执行(类似 finally,用于资源清理)。
  4. 不要做耗时操作
    • 拦截器主要用于请求校验、日志记录等,不适合做数据库查询、复杂业务逻辑,否则会影响性能。
  5. 与 Filter 区别
    • Filter 是 Servlet 规范的一部分,作用在 DispatcherServlet 之前,能拦截所有请求(包括静态资源)。
    • Interceptor 是 Spring MVC 的扩展,只能拦截 Controller 请求(不会拦截静态资源),功能更贴近业务。

✅ 总结:
拦截器的开发步骤

  1. 实现 HandlerInterceptor 接口
  2. 在配置类中注册拦截器及其作用范围
  3. preHandlepostHandleafterCompletion 中编写逻辑

注意事项:执行顺序、返回值控制、异常处理、避免耗时操作,以及和 Filter 的区别。

Prompt:基于过滤器和拦截器实现令牌校验的区别

Response:

好问题 👍,这个在做 Token(JWT 等)校验时很常见。我们来对比一下 基于过滤器(Filter)基于拦截器(Interceptor) 实现令牌校验的区别:


1. 执行位置和时机不同

特性 过滤器(Filter) 拦截器(Interceptor)
所属层次 Servlet 规范(容器级别) Spring MVC(框架级别)
执行时机 请求进入 Servlet 前 就执行 进入 Controller 前 才执行
能拦截的范围 所有请求(包括静态资源、图片、CSS、JS) 仅拦截 Controller 请求,不拦截静态资源

👉 区别

  • Filter 更底层,适合做通用处理,比如统一 Token 校验、跨域、编码处理。
  • Interceptor 更贴近业务,只处理业务接口的 Token 校验,不会管静态资源。

2. 配置方式不同

  • Filter
    • 通过 @WebFilterweb.xml 配置 URL 模式。
    • 执行 chain.doFilter(request, response) 进行放行。
  • Interceptor
    • 在 Spring 配置类里通过 addInterceptors() 注册,并设置拦截路径、排除路径。
    • 通过 preHandle() 返回 true/false 控制是否放行。

3. Token 校验实现对比

基于 Filter 的 Token 校验

  • doFilter() 中获取请求头 Authorization,解析并验证 Token。
  • 校验失败 → 直接写响应(401 Unauthorized),不再调用 chain.doFilter()
  • 校验成功 → 调用 chain.doFilter() 继续后续流程。

特点

  • 粒度更粗,能拦截所有请求。
  • 通常用于 统一网关层面(API 网关、微服务入口)。

基于 Interceptor 的 Token 校验

  • preHandle() 中获取请求头 Authorization,解析并验证 Token。
  • 校验失败 → 返回 false,写入错误响应,拦截请求。
  • 校验成功 → 返回 true,继续执行 Controller。

特点

  • 粒度更细,只拦截业务接口请求。
  • 常用于 应用内部,比如某些接口需要校验 Token,某些接口(如登录、注册)放行。

4. 性能和适用场景

对比维度 Filter Interceptor
性能消耗 较低(容器层面,早期执行) 较高(Spring MVC 层面,晚一点执行)
适用场景 全局统一 Token 校验、跨域、日志、限流 业务接口级别的 Token 校验、权限控制
静态资源 会拦截(需手动排除) 默认不拦截
微服务/网关 常用(统一鉴权) 较少用

5. 总结

  • Filter 版 Token 校验:更底层,适合全局、通用性强的场景,比如统一网关鉴权。
  • Interceptor 版 Token 校验:更靠近业务层,适合在应用中做接口级别的权限控制。

👉 常见实践是:

  • 网关层:用 Filter 做统一 Token 校验。
  • 应用层:用 Interceptor 结合注解(如 @LoginRequired)做细粒度的接口权限控制。

Prompt:如何基于拦截器实现令牌校验

Response:

👌 我来给你整理一下 基于拦截器实现令牌(JWT 等)校验 的完整思路和步骤。


一、基本思路

  1. 请求进入时,拦截器的 preHandle() 会先执行。
  2. preHandle() 中获取请求头里的 Authorization(或自定义头)。
  3. 解析并校验 Token(签名、过期时间等)。
    • 如果校验失败:返回 false,拦截请求,直接返回 401 响应。
    • 如果校验成功:把解析出的用户信息放到 request 或上下文中,继续放行。

二、实现步骤

1. 创建拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class TokenInterceptor implements HandlerInterceptor {

private static final String SECRET_KEY = "mySecretKey123"; // 与生成JWT时一致

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 从请求头获取 Token
String authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("Missing or invalid Authorization header");
return false;
}

String token = authHeader.substring(7); // 去掉 "Bearer "

try {
// 解析 Token
Claims claims = Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();

// 将用户信息存入 request,方便后续 Controller 使用
request.setAttribute("userId", claims.getSubject());
request.setAttribute("role", claims.get("role"));
return true;

} catch (Exception e) {
// Token 无效或过期
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("Invalid or expired token");
return false;
}
}
}

2. 注册拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TokenInterceptor())
.addPathPatterns("/api/**") // 需要校验的接口
.excludePathPatterns("/api/login", "/api/register"); // 放行的接口
}
}

3. 控制器使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

@RestController
public class UserController {

@GetMapping("/api/userinfo")
public String getUserInfo(HttpServletRequest request) {
String userId = (String) request.getAttribute("userId");
String role = (String) request.getAttribute("role");
return "当前用户ID: " + userId + ",角色: " + role;
}
}

三、注意事项

  1. Token 放置位置
    • 一般在请求头 Authorization: Bearer <token> 中。
    • 也可以放在 Cookie 或 Query 参数,但请求头更安全。
  2. 拦截路径配置
    • 建议只拦截需要鉴权的业务接口,如 /api/**
    • 登录、注册、静态资源要放行。
  3. 安全性
    • 秘钥(SECRET_KEY)要保密,生产环境可放配置中心。
    • Token 有效期不要太长,避免泄露风险。
    • 可以设计 刷新 Token黑名单机制,支持主动注销。
  4. 与注解结合
    • 可自定义 @LoginRequired 注解,在拦截器里结合 HandlerMethod 判断哪些接口需要校验 Token,哪些不需要。

总结
基于拦截器实现令牌校验的核心是:

  • preHandle() 中获取 Token → 校验签名和有效期 → 失败返回 401 → 成功放行并传递用户信息。

Prompt:根据以上对话简短总结一下我学了什么

Response:

你这段学习主要掌握了 会话跟踪与令牌校验 的核心知识:

  1. 会话跟踪方案
    • Cookie、Session、令牌(Token / JWT)的原理、优缺点。
  2. JWT 使用方法
    • 登录成功后生成 Token,客户端保存并随请求发送,服务端解析校验。
  3. 登录校验方案
    • 基于 Cookie / Session、Token、JWT 的不同实现思路。
  4. Filter 与 Interceptor
    • Filter:Servlet 层,拦截所有请求,适合全局处理。
    • Interceptor:Spring MVC 层,拦截业务接口,适合细粒度控制。
  5. 基于拦截器的令牌校验
    • preHandle() 获取请求头 Token → 校验签名和有效期 → 失败返回 401 → 成功放行并传递用户信息。
    • 可结合路径配置或注解实现灵活控制。