请求拦截器
拦截器概述
在基于 Tio-Boot 的 HTTP 应用中,拦截器(Interceptor)是一种在请求到达业务处理器之前或响应返回客户端之后,对请求/响应进行预处理和后处理的机制。合理地使用拦截器,可用于集中化地处理以下场景:
- 请求校验:验证 HTTP 请求是否合法,拦截恶意或缺少关键信息的请求
- 鉴权与权限控制:检查请求的认证信息或用户权限
- 日志记录:统一记录请求与响应的元数据
- 性能监控:统计方法执行时间或请求耗时
- 限流与防刷:对频繁请求或异常请求进行限流或封禁
Interceptor & Handler Design
HttpRequestValidationInterceptor 请求校验拦截器实现
- 作用:对所有请求生效。
- 功能:校验请求头是否合法,例如检查是否携带
version
请求头。
AuthTokenInterceptor 用户Token拦截器
- 作用:对所有请求生效。
- 功能:解析请求头中的
token
参数,提取userId
并写入request
上下文。
UserTokenInterceptor (属于 HttpRequestInterceptor) 通用拦截器
- 作用:仅对指定配置的请求路径生效。
- 功能:校验
userId
是否存在于数据库中。
Handler/Controller 层逻辑
- 说明:某些 Endpoint 同时支持 带用户 ID 和 不带用户 ID 的两种访问方式。
- 因此在 Handler 或 Controller 的 Action 中,可能仍需对
userId
是否存在进行额外判断。
示例代码:
String userIdString = TioRequestContext.getUserIdString(); if (userIdString == null) { httpResponse.setStatus(401); return httpResponse; }
普通拦截器
一、概述
在 Tio‑Boot 中,HTTP 拦截器(Interceptor)可用于对进出的请求进行统一控制,例如:权限校验、日志记录、限流等。通过配置拦截器模型(HttpInterceptorModel
),您可以灵活地指定哪些 URL 应被拦截(blockedUrls
),哪些 URL 应被放行(allowedUrls
),从而实现精细化的请求管理。
二、核心类说明
1. HttpInterceptorModel
拦截器的配置模型,包含:
- 名称
name
:拦截器标识。 - 放行列表
allowedUrls
:一组明确允许的 URL 模式。 - 拦截列表
blockedUrls
:一组需要拦截的 URL 模式。 - 拦截器实例
interceptor
:实现HttpRequestInterceptor
接口的处理逻辑。
常用方法:
// 设置名称
HttpInterceptorModel model = new HttpInterceptorModel()
.setName("exampleInterceptor");
// 添加单个/多个放行 URL
model.addAllowUrl("/admin/login");
model.addAllowUrls("/public/**", "/status");
// 添加单个/多个拦截 URL
model.addBlockUrl("/admin/**");
model.addAllowUrls("/api/private/**", "/secure/*");
// 绑定拦截器实现
model.setInterceptor(new YourCustomInterceptor());
注意
- 同一路径不能同时出现在
allowedUrls
和blockedUrls
中。- 支持通配符:
/**
匹配所有子路径,/*
匹配一级子路径。
2. HttpInteceptorConfigure
拦截器配置管理器,用于集中管理所有拦截器模型。
HttpInteceptorConfigure configure = new HttpInteceptorConfigure();
configure.add(model); // 添加拦截器
configure.remove("name"); // 按名称移除拦截器
List<HttpInterceptorModel> list = configure.getInteceptors(); // 获取所有模型
3. DefaultHttpRequestInterceptorDispatcher
执行链中的调度器,负责遍历所有 HttpInterceptorModel
,并在请求处理前后调用相应的拦截器方法。
doBeforeHandler
:在业务处理前执行。- 若返回非
null
的HttpResponse
,则后续业务和拦截器链将被短路。
- 若返回非
doAfterHandler
:在业务处理后执行。
三、在 TioBootServer 中注册拦截器
在启动类或配置类中,通过 TioBootServer
将自定义的拦截器配置注入到服务器:
import com.litongjava.annotation.AConfiguration;
import com.litongjava.annotation.Initialization;
import com.litongjava.tio.boot.http.interceptor.HttpInteceptorConfigure;
import com.litongjava.tio.boot.http.interceptor.HttpInterceptorModel;
import com.litongjava.tio.boot.server.TioBootServer;
@AConfiguration
public class InterceptorConfiguration {
@Initialization
public void init() {
// 1. 构建一个全局拦截器模型
HttpInterceptorModel global = new HttpInterceptorModel()
.setName("globalInterceptor")
.addBlockUrl("/**") // 拦截所有路径
.addAllowedUrl("/health") // 放行健康检查接口
.setInterceptor(new GlobalInterceptor());
// 2. 创建配置管理器并添加模型
HttpInteceptorConfigure config = new HttpInteceptorConfigure();
config.addInterceptor(global);
// 3. 注入到 TioBootServer
TioBootServer.me().setHttpInteceptorConfigure(config);
}
}
四、自定义拦截器示例
自定义拦截器需实现 HttpRequestInterceptor
接口,示例中将请求日志打印到控制台:
package com.litongjava.demo.interceptor;
import com.litongjava.tio.http.common.HttpRequest;
import com.litongjava.tio.http.common.HttpResponse;
import com.litongjava.tio.http.common.RequestLine;
import com.litongjava.tio.http.server.intf.HttpRequestInterceptor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class GlobalInterceptor implements HttpRequestInterceptor {
@Override
public HttpResponse doBeforeHandler(
HttpRequest request,
RequestLine requestLine,
HttpResponse originalResponse) throws Exception
{
log.info("[Before] {} {}", requestLine.getMethod(), requestLine.getPath());
// 返回 null 表示继续执行后续拦截器和业务处理
return null;
}
@Override
public void doAfterHandler(
HttpRequest request,
RequestLine requestLine,
HttpResponse response,
long costMillis) throws Exception
{
log.info("[After] {} {} -> {} ({} ms)",
requestLine.getMethod(),
requestLine.getPath(),
response.getStatus(),
costMillis);
}
}
五、URL 匹配与执行逻辑
匹配顺序
DefaultHttpRequestInterceptorDispatcher
会依次遍历所有已注册的HttpInterceptorModel
。拦截判定
- 若请求 URL 与某个模型的
blockedUrls
任一模式匹配,且不在该模型的allowedUrls
中,则视为需拦截。 - 在
doBeforeHandler
中若返回非空HttpResponse
,则直接返回该响应,不再执行后续拦截器与业务逻辑。这一点很重要,和其它web框架不同,如果想要后续的拦截器和业务逻辑执行,务必返回null
- 若请求 URL 与某个模型的
通配符规则
/api/**
:匹配/api
及其所有子路径。/static/*
:仅匹配一级子路径,如/static/logo.png
。
六、最佳实践
- 分层注册:可根据业务模块,将拦截器按职责拆分成多个模型,如认证、授权、日志、限流等,按需组合。
- 精确放行:尽量将
allowedUrls
配置得更精细,例如放行静态资源、健康检查、登录回调等。 - 性能考量:拦截器链中的执行逻辑应尽量轻量,避免阻塞或耗时操作;如需复杂处理,可异步执行或交由专用服务。
- 单元测试:对自定义拦截器逻辑编写单元测试,验证不同 URL 下的拦截与放行行为。
七、总结
通过以上配置,您可以在 Tio‑Boot 应用中轻松地管理 HTTP 请求的拦截与放行,实现统一的安全、限流、监控等功能。拦截器模型与调度器解耦,配置灵活且可扩展。实践中可结合业务需求,定制多层拦截策略,进一步提升系统的健壮性与可维护性。
请求校验拦截器实现
本章将重点介绍如何在 Tio-Boot 中编写和配置“请求校验拦截器”(HttpRequestValidationInterceptor
),实现对缺失必填 Header 的拦截、封禁高频“无头”请求的功能,并演示如何在启动配置类中注册该拦截器。
1. 设计思路
- 无头请求判定:当请求缺少关键 Header(例如
from
)时,认为该请求不合法。 - 计数与阈值:统计同一 IP 的“无头请求”次数,超过设定阈值(如 10 次)后,将该 IP 加入黑名单(可扩展为持久化到 Redis 或防火墙规则)。
- 返回响应:对于每一次无头请求,都返回 HTTP 403 并主动关闭连接。
2. 自定义拦截拦截器校验逻辑
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import com.litongjava.tio.http.common.HttpRequest;
import com.litongjava.tio.http.common.HttpResponse;
import com.litongjava.tio.http.common.RequestLine;
import com.litongjava.tio.http.server.intf.HttpRequestInterceptor;
/**
* 应用级请求校验拦截器
* 1. 检查请求是否包含必要 Header
* 2. 累计无头请求次数,超过阈值则封禁 IP
*/
public class AppRequestValidationInterceptor implements HttpRequestInterceptor {
// 存储每个 IP 的无头请求计数
private final ConcurrentMap<String, AtomicInteger> badCount = new ConcurrentHashMap<>();
private static final int MAX_BAD = 10;
@Override
public HttpResponse doBeforeHandler(HttpRequest request, RequestLine requestLine, HttpResponse httpResponse) throws Exception {
String from = request.getHeader("from");
String clientIp = request.channelContext.getClientNode().getIp();
// 如果缺少 from Header,则视为无头请求
if (from == null) {
// 累加计数
int count = badCount.computeIfAbsent(clientIp, k -> new AtomicInteger()).incrementAndGet();
// 返回 403,主动关闭连接
httpResponse.setStatus(403);
httpResponse.setHeader("Connection", "close");
httpResponse.setKeepConnection(false);
// 超过阈值,执行封禁逻辑
if (count >= MAX_BAD) {
blockIp(clientIp);
}
return httpResponse;
} else {
//必须返回null,让请求继续向后执行
return null;
}
}
/**
* 封禁 IP(可根据实际需求扩展为:加入 Redis、更新防火墙规则等)
*/
private void blockIp(String ip) {
// TODO: 将 IP 持久化到黑名单,比如 Redis 或防火墙规则
}
@Override
public void doAfterHandler(HttpRequest request, RequestLine requestLine, HttpResponse response, long cost) {
// 后置处理:目前无操作
}
}
3. 在 Tio-Boot 中配置与使用
为了使上述拦截器生效,需要在应用启动配置类中注册它。Tio-Boot 提供了通过注解和初始化方法来完成这一操作的能力。
import com.litongjava.annotation.AConfiguration;
import com.litongjava.annotation.Initialization;
import com.litongjava.tio.boot.server.TioBootServer;
@AConfiguration
public class InterceptorConfiguration {
/**
* 应用初始化时注册请求校验拦截器
*/
@Initialization
public void config() {
// 获取 Tio-Boot 服务器单例
TioBootServer server = TioBootServer.me();
// 创建并注册自定义拦截器
AppRequestValidationInterceptor validationInterceptor = new AppRequestValidationInterceptor();
server.setHttpRequestValidationInterceptor(validationInterceptor);
}
}
说明:
@AConfiguration
标记该类为配置类,需被框架扫描。@Initialization
标记的方法在应用启动时被调用。TioBootServer.me()
返回 TioBootServer 单例,setHttpRequestValidationInterceptor(...)
用于替换默认的请求校验拦截器。
4.拓展与优化
黑名单持久化
- 将被封禁的 IP 写入 Redis、数据库或防火墙规则,重启后依然生效;
- 定期清理过期或临时封禁 IP。
动态阈值与告警
- 根据应用运行时负载或安全策略动态调整
MAX_BAD
; - 当某个 IP 次数激增时,可触发告警通知运维人员。
- 根据应用运行时负载或安全策略动态调整
白名单机制
- 对某些受信任的 IP 段或内网地址跳过校验。
多维度安全校验
- 除了 Header 校验,还可结合签名校验、令牌校验或验证码等手段;
- 对请求频率、请求体大小、URL 路径等进行限流或过滤。
5.小结
本文介绍了如何在 Tio-Boot 框架中编写一个“请求校验拦截器”,该拦截器能够在请求到达业务处理器前,验证请求是否合法、统计无头请求并在超过阈值后封禁 IP。通过注解式配置,可轻松将该拦截器集成到项目中。实际生产环境中,可根据业务需求进一步扩展持久化、告警及白名单等功能,以提升应用的安全防护能力。
好的 👍 我帮你把这部分整理润色成一份完整的文档,加上背景、用途和目标说明,让它更像一篇可以直接放在开发文档里的技术说明。
使用 AuthTokenInterceptor 和 UserTokenInterceptor 的实践指南
背景
在基于 Tio-Boot 框架的 Web 应用中,接口安全和用户身份管理是后端系统设计的核心问题。 常见需求包括:
- 请求头校验:确保请求头中包含必要参数(如
version
)。 - 身份认证:通过请求头中的
token
校验用户是否合法。 - 用户上下文管理:在请求的生命周期中安全、统一地传递用户 ID。
- 权限隔离:支持部分接口无需登录即可访问,其他接口则需要合法用户身份。
为此,Tio-Boot 提供了 AuthTokenInterceptor 和 UserTokenInterceptor 两个内置拦截器,它们配合使用可以满足上述需求。
目标
- 统一 Token 验证逻辑:支持系统管理员 Token 和普通用户 Token 的验证。
- 透明的用户上下文传递:通过拦截器将
userId
注入到TioRequestContext
,在 Controller 中直接获取。 - 可配置化:允许定义无需拦截的 URL(如登录、注册、验证码接口),同时灵活扩展 Token 验证逻辑。
- 安全性:对非法请求(无 Token 或 Token 校验失败)统一拦截,返回
401 Unauthorized
。
拦截器职责分工
AuthTokenInterceptor
负责 验证 Token 合法性。
支持两类 Token:
- 系统管理员 Token:从环境变量获取并比对。
- 用户 JWT Token:通过
JwtUtils
验证签名与有效期,并解析userId
。
验证通过后,将用户 ID 写入
TioRequestContext
。
UserTokenInterceptor
- 负责 拦截请求并进行用户校验。
- 默认拦截所有 URL (
/**
),但支持配置 白名单 URL,如登录、注册、验证码、静态资源等接口。 - 如果请求未携带有效 Token,则阻止请求继续下发。
最佳实践实例
1. 定义白名单 URL
public interface TioBootAdminUrls {
String[] ALLLOW_URLS = {
"", "/",
"/api/event/add",
"/register/*", "/api/login/account", "/api/login/outLogin",
"/api/v1/login", "/api/v1/register", "/api/v1/user/referesh",
"/api/v1/sendVerification", "/api/v1/sendVerificationCode", "/api/v1/verify",
"/verification/email", "/api/v1/user/resetPassword", "/api/v1/anonymous/create",
"/api/v1/google/login", "/api/v1/user/refresh",
"/table/json/tio_boot_admin_system_article/get/*",
"/table/json/tio_boot_admin_system_docx/get/*",
"/table/json/tio_boot_admin_system_pdf/get/*"
};
}
2. 自定义 Token 验证逻辑
package com.litongjava.tio.boot.admin.services;
import java.util.function.Predicate;
import com.litongjava.tio.boot.admin.costants.AppConstant;
import com.litongjava.tio.boot.http.TioRequestContext;
import com.litongjava.tio.utils.environment.EnvUtils;
import com.litongjava.tio.utils.jwt.JwtUtils;
public class TioBootAdminTokenPredicate implements Predicate<String> {
@Override
public boolean test(String token) {
String adminToken = EnvUtils.get(AppConstant.ADMIN_TOKEN);
//system token
if (adminToken != null && adminToken.equals(token)) {
TioRequestContext.setUserId(0L);
return true;
}
// user and admin token
String key = EnvUtils.getStr(AppConstant.ADMIN_SECRET_KEY);
boolean verify = JwtUtils.verify(key, token);
if (verify) {
String userId = JwtUtils.parseUserIdString(token);
TioRequestContext.setUserId(userId);
return true;
}
return false;
}
public Long parseUserIdLong(String token) {
String key = EnvUtils.getStr(AppConstant.ADMIN_SECRET_KEY);
boolean verify = JwtUtils.verify(key, token);
if (verify) {
return JwtUtils.parseUserIdLong(token);
}
return null;
}
}
3. 配置拦截器
package com.litongjava.tio.boot.admin.config;
import java.util.function.Predicate;
import com.litongjava.tio.boot.admin.costants.TioBootAdminUrls;
import com.litongjava.tio.boot.admin.services.TioBootAdminTokenPredicate;
import com.litongjava.tio.boot.http.interceptor.HttpInteceptorConfigure;
import com.litongjava.tio.boot.http.interceptor.HttpInterceptorModel;
import com.litongjava.tio.boot.server.TioBootServer;
import com.litongjava.tio.boot.token.AuthTokenInterceptor;
import com.litongjava.tio.boot.token.UserTokenInterceptor;
public class TioAdminInterceptorConfiguration {
private String[] permitUrls;
private boolean alloweStaticFile;
private Predicate<String> validateTokenLogic;
public TioAdminInterceptorConfiguration() {
}
public TioAdminInterceptorConfiguration(String[] permitUrls) {
this.permitUrls = permitUrls;
}
public TioAdminInterceptorConfiguration(String[] permitUrls, Predicate<String> validateTokenLogic) {
this.permitUrls = permitUrls;
this.validateTokenLogic = validateTokenLogic;
}
public TioAdminInterceptorConfiguration(String[] permitUrls, boolean b) {
this.permitUrls = permitUrls;
this.alloweStaticFile = b;
}
public void config() {
// token验证逻辑
if (validateTokenLogic == null) {
validateTokenLogic = new TioBootAdminTokenPredicate();
}
AuthTokenInterceptor authTokenInterceptor = new AuthTokenInterceptor(validateTokenLogic);
TioBootServer.me().setAuthTokenInterceptor(authTokenInterceptor);
UserTokenInterceptor userTokenInterceptor = new UserTokenInterceptor();
HttpInterceptorModel model = new HttpInterceptorModel();
model.setInterceptor(userTokenInterceptor);
// 拦截所有路由
model.addBlockUrl("/**");
// 添加不拦截的路由
model.addAllowUrls(TioBootAdminUrls.ALLLOW_URLS);
if (permitUrls != null) {
model.addAllowUrls(permitUrls);
}
model.setAlloweStaticFile(alloweStaticFile);
HttpInteceptorConfigure inteceptorConfigure = new HttpInteceptorConfigure();
inteceptorConfigure.add(model);
// 将拦截器配置添加到 Tio 服务器
TioBootServer.me().setHttpInteceptorConfigure(inteceptorConfigure);
}
}
Controller 层的使用
在 Controller 或 Handler 中,可以直接通过 TioRequestContext
获取用户 ID:
String userIdString = TioRequestContext.getUserIdString();
if (userIdString == null) {
httpResponse.setStatus(401);
return httpResponse;
}
// 根据 userId 执行业务逻辑
⚠️ 注意:部分接口既允许匿名访问,也允许带用户 ID 的访问,需要开发者在 Controller 中做进一步的判断。
总结
通过 AuthTokenInterceptor 与 UserTokenInterceptor 的配合使用,Tio-Boot 提供了一套灵活、安全的 Token 验证与用户上下文管理机制,其优势包括:
- 支持管理员和普通用户的双重 Token 验证。
- 请求拦截与 Token 验证解耦,职责清晰。
- 可配置白名单 URL,满足开放接口需求。
- 在 Controller 中使用简单,提升开发效率。
这套机制既能保证系统的安全性,又为开发者提供了灵活的配置和扩展能力。