You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在服务层切面通知中获取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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 16:49:52