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

如何在Micronaut过滤器中为传入请求添加自定义请求头

问题解答

一、decoration(装饰)和modification(修改)的区别

  • modification(修改):指直接修改原有请求/响应对象的内部属性。Micronaut 设计上默认 HttpRequest 为不可变对象,直接修改原对象会触发 UnsupportedOperationException,也就是你遇到的报错。
  • decoration(装饰):指不改动原有请求/响应对象本身,通过装饰器模式生成一个新的包装对象,在包装对象中扩展你需要的属性(比如新增请求头),再将包装对象传递给过滤器链后续节点,原请求对象全程保持不变,符合框架的不可变设计规范。

二、实现JWT校验并新增请求头的方案

你之前遇到mutate()方法抛异常,是因为你主动把原请求包装成了HttpRequestWrapper,这个类没有重写mutate()默认实现,才触发了报错。直接使用原生请求的mutate()方法即可,Micronaut 内置的请求实现已经重写了该方法,会返回一个新的可变请求实例,符合装饰的要求。
完整实现代码如下:

1. 过滤器实现

@Filter(Filter.MATCH_ALL_PATTERN)
public class SecurityFilter implements HttpServerFilter {

    private static final Logger LOG = LoggerFactory.getLogger(SecurityFilter.class);
    // 注入你自己实现的JWT校验工具类
    private final JwtValidator jwtValidator;

    public SecurityFilter(JwtValidator jwtValidator) {
        this.jwtValidator = jwtValidator;
    }

    @Override
    public Publisher<MutableHttpResponse<?>> doFilter(final HttpRequest<?> request, final ServerFilterChain chain) {
        // 从请求头提取Bearer Token
        Optional<String> tokenOpt = request.getHeaders().getAuthorization()
                .filter(authStr -> authStr.startsWith("Bearer "))
                .map(authStr -> authStr.substring(7));
        
        // 无有效token直接返回401
        if (tokenOpt.isEmpty()) {
            return Publishers.just(HttpResponse.status(HttpStatus.UNAUTHORIZED));
        }

        try {
            // 校验JWT合法性,提取租户ID字段,这里逻辑你可以根据自己的JWT结构调整
            String tenantId = jwtValidator.validateAndGetTenantId(tokenOpt.get());
            // 装饰原请求,新增X-Tenant-Id头,不会改动原请求对象
            HttpRequest<?> decoratedRequest = request.mutate()
                    .header("X-Tenant-Id", tenantId);
            // 将装饰后的请求传入过滤器链
            return chain.proceed(decoratedRequest);
        } catch (Exception e) {
            // JWT校验失败返回401
            LOG.warn("JWT 校验不通过", e);
            return Publishers.just(HttpResponse.status(HttpStatus.UNAUTHORIZED));
        }
    }

    @Override
    public int getOrder() {
        return ServerFilterPhase.SECURITY.order();
    }

}

2. 控制器取值

你原有控制器的代码不需要做任何改动,@Header("X-Tenant-Id") 可以直接读取到过滤器中新增的请求头:

@Get(uri = "/{id}", produces = MediaType.APPLICATION_JSON)
public Optional<Item> findById(@PathVariable("id") final String id, @Header("X-Tenant-Id") final String tenantId) {
    return service.findById(id, tenantId);
}

内容的提问来源于stack exchange,提问作者jokarl

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 10:36:03