如何在服务层切面通知中获取HttpServletRequest
问题分析
你的项目使用的是JAX-RS(从控制器的@Path、@GET等注解可判断),而Spring的RequestContextHolder是专为Spring MVC设计的请求上下文管理工具,无法识别JAX-RS的请求链路,所以才会出现获取到null的情况,这是之前所有方案失效的核心原因。
解决方案
方案一:JAX-RS过滤器+ThreadLocal传递请求
通过JAX-RS过滤器拦截请求,将HttpServletRequest存入线程本地变量,切面从该变量中获取请求信息,用完后清理避免线程池复用问题。
1. 自定义请求上下文存储类
public class RequestContextHolder { private static final ThreadLocal<HttpServletRequest> requestHolder = new ThreadLocal<>(); public static void setRequest(HttpServletRequest request) { requestHolder.set(request); } public static HttpServletRequest getRequest() { return requestHolder.get(); } public static void clear() { requestHolder.remove(); } }
2. 实现JAX-RS容器过滤器
@Provider public class RequestContextFilter implements ContainerRequestFilter { @Context private HttpServletRequest httpServletRequest; @Override public void filter(ContainerRequestContext requestContext) throws IOException { RequestContextHolder.setRequest(httpServletRequest); // 请求结束后自动清理ThreadLocal requestContext.register(() -> RequestContextHolder.clear()); } }
3. 修改切面类
@Component @Aspect public class AccessAdvice { @Autowired private AccessTokenSecretConfig accessTokenSecretConfig; public static final String ACCESS_TOKEN_HEADER = "accessToken"; @Pointcut("@annotation(com.company.services.annotations.EnableAuth)") public void invokeAnnotation() {} @Before("invokeAnnotation()") public void doBefore(final JoinPoint joinPoint) throws IOException { HttpServletRequest req = RequestContextHolder.getRequest(); if (req == null) { throw new IllegalStateException("无法获取当前请求,请检查过滤器是否生效"); } final String accessToken = req.getHeader(ACCESS_TOKEN_HEADER); // 后续权限校验逻辑... } }
4. 注册JAX-RS过滤器
如果是Spring XML配置,添加过滤器Bean:
<bean id="requestContextFilter" class="com.company.services.filters.RequestContextFilter"/>
如果是Jersey的ResourceConfig配置,直接注册过滤器:
public class JerseyConfig extends ResourceConfig { public JerseyConfig() { register(RequestContextFilter.class); // 其他控制器、组件注册... } }
方案二:通过RequestContextWrapper传递请求
利用已有的RequestContextWrapper封装请求信息,在控制器层注入HttpServletRequest后传递到服务层,切面直接从参数中获取。
1. 改造RequestContextWrapper
public class RequestContextWrapper { @Context private HttpServletRequest request; public HttpServletRequest getRequest() { return request; } }
2. 修改切面切点与逻辑
@Component @Aspect public class AccessAdvice { @Autowired private AccessTokenSecretConfig accessTokenSecretConfig; public static final String ACCESS_TOKEN_HEADER = "accessToken"; @Pointcut("@annotation(com.company.services.annotations.EnableAuth) && args(contextWrapper, ..)") public void invokeAnnotation(RequestContextWrapper contextWrapper) {} @Before("invokeAnnotation(contextWrapper)") public void doBefore(final JoinPoint joinPoint, RequestContextWrapper contextWrapper) throws IOException { HttpServletRequest req = contextWrapper.getRequest(); if (req == null) { throw new IllegalStateException("无法获取当前请求"); } final String accessToken = req.getHeader(ACCESS_TOKEN_HEADER); // 后续权限校验逻辑... } }
注意事项
- 禁止在JAX-RS项目中使用Spring的
RequestContextHolder获取请求,两者上下文机制不兼容。 - 使用ThreadLocal时必须确保请求结束后清理,避免线程池复用导致的请求信息泄露。
- 确认JAX-RS过滤器已被正确注册,否则无法拦截请求并设置线程变量。
内容的提问来源于stack exchange,提问作者user2594
相关产品推荐
相关产品推荐

