目录
方案 1:SpringCloud Gateway 全局过滤器(推荐)
1)黑名单存储
2)自定义 GlobalFilter
动态拉黑(Redis 版本)
方案 2:如果没有网关,在微服务内部拦截 HandlerInterceptor
方案 3:AOP 切面拦截(不推荐)
重要细节
选型总结
需求:拦截指定 1~2 个用户,禁止访问项目所有接口。
区分:用户身份,不是客户端 IP;一般根据
userId / token解析出来的用户ID做黑名单。
可选实现方案优先级(从推荐到简单粗暴):
- Gateway 全局过滤器(首选,网关层拦截,请求不转发到微服务)
- 微服务内部全局拦截器 HandlerInterceptor
- AOP 切面拦截所有 Controller
- Spring Security 过滤器链
最佳实践:放在Spring Cloud Gateway 网关做拦截,请求直接在网关拒绝,不打到后端业务服务,节省资源。
方案 1:SpringCloud Gateway 全局过滤器(推荐)
网关层,所有微服务请求统一在这里校验黑名单。
1)黑名单存储
少量用户,可以内存 Set;生产建议放 Redis,方便动态增删拉黑用户,不用重启网关。
java
// 内存黑名单,测试用 private final Set<String> blackUserIdSet = Set.of("10001","10086");生产环境使用 Redis
Set结构,key:black:user:list
2)自定义 GlobalFilter
网关拿到 token 解析出 userId(你的业务逻辑解析 token),判断是否在黑名单,命中直接返回 403。
java
import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.annotation.Order; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.nio.charset.StandardCharsets; import java.util.Set; @Component @Order(Integer.MIN_VALUE) // 优先级最高,最先执行 public class UserBlackListGlobalFilter implements GlobalFilter { // 黑名单用户ID,少量用户可以内存存放;生产使用Redis private final Set<String> blackUserIdSet = Set.of("10001", "10086"); @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); // 1. 从header获取token,根据你的业务解析token得到userId String token = request.getHeaders().getFirst("Authorization"); String userId = parseUserIdByToken(token); // 【需要你自己实现token解析逻辑】 // 2. 判断用户是否黑名单 if(userId != null && blackUserIdSet.contains(userId)){ ServerHttpResponse response = exchange.getResponse(); response.setStatusCode(HttpStatus.FORBIDDEN); response.getHeaders().setContentType(MediaType.APPLICATION_JSON); String respJson = "{\"code\":403,\"msg\":\"您已被禁止访问系统\",\"data\":null}"; DataBuffer buffer = response.bufferFactory().wrap(respJson.getBytes(StandardCharsets.UTF_8)); return response.writeWith(Flux.just(buffer)); } // 放行 return chain.filter(exchange); } /** * 解析token获取userId,示例占位,替换成你项目真实逻辑 */ private String parseUserIdByToken(String token){ if(token == null){ return null; } // return JwtUtil.getUserId(token); return null; } }⚠️注意:Gateway 是 WebFlux 响应式环境,不能用 Servlet 相关 API。
动态拉黑(Redis 版本)
把黑名单放到 Redis,网关读取,支持运行时新增 / 删除黑名单用户,无需重启网关:
java
// 判断逻辑替换 String userId = parseUserIdByToken(token); if(userId != null && Boolean.TRUE.equals(redisTemplate.opsForSet().isMember("black:user:list", userId))){ // 返回403禁止访问 }方案 2:如果没有网关,在微服务内部拦截 HandlerInterceptor
SpringMVC 环境,普通微服务,所有 Controller 接口拦截。
java
import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Set; @Component public class UserBlackInterceptor implements HandlerInterceptor { private final Set<String> blackUserIdSet = Set.of("10001","10086"); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("Authorization"); String userId = parseTokenGetUserId(token); if(userId != null && blackUserIdSet.contains(userId)){ response.setStatus(403); response.setContentType("application/json;charset=utf‑8"); ObjectMapper mapper = new ObjectMapper(); mapper.writeValue(response.getWriter(), Map.of("code",403,"msg","禁止访问")); return false; // false:拦截,不进入controller } return true; } private String parseTokenGetUserId(String token){ // 业务解析token return null; } }注册拦截器:
java
@Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private UserBlackInterceptor userBlackInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(userBlackInterceptor) .addPathPatterns("/**"); // 拦截全部接口 } }缺点:如果多微服务,每个服务都要配置拦截器;优先网关统一处理。
方案 3:AOP 切面拦截(不推荐)
拦截 @Controller/@RestController 所有方法。
缺点:异常接口、过滤器、静态资源、response 直接输出场景 AOP 切不到,有漏网接口。只适合临时应急。
重要细节
- 白名单:内部健康检查接口 /actuator/网关过滤器中可以放过健康检查,避免拉黑逻辑把服务监控搞挂:
java
String path = request.getPath().value(); if(path.startsWith("/actuator/")){ return chain.filter(exchange); }- 区分:拉黑用户 IDvs 拉黑 IP。你的场景是用户,不要用 IP。
- token 为空(未登录请求)直接放行,黑名单只针对登录用户。
- 少量黑名单用户内存 Set 够用;后续黑名单变多一定要迁移 Redis,支持运营后台拉黑用户。
选型总结
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| Gateway 全局 Filter | SpringCloud 网关架构✅ | 请求在网关拦截,不转发后端,性能最好,统一管控 | 需要网关服务 |
| HandlerInterceptor | 无网关,微服务内部 | 简单,SpringMVC 原生 | 多微服务需要每个服务配置 |
| AOP 切面 | 临时快速实现 | 编码简单 | 存在部分接口拦截失效风险 |
如果你现在项目有 SpringCloud Gateway,直接用方案 1,这是生产标准做法。