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

为何@RestCookie无法检测resteasy-reactive中ContainerRequestFilter设置的Cookie头?

Resteasy Reactive中JWT Cookie过滤器问题解析与优化方案

一、@RestCookie与@CookieParam表现差异的原因

1. 架构底层差异

  • Resteasy Classic基于Servlet同步栈,ContainerRequestFilter修改请求头后,JAX-RS的参数绑定机制(包括@CookieParam)会重新读取修改后的请求头,因此能获取到过滤器添加的Cookie。
  • Resteasy Reactive基于Vert.x非阻塞响应式栈,请求的Cookie集合在进入过滤器之前就已被解析并缓存到ServerRequest对象中。后续在ServerRequestFilter中修改请求头,不会触发Cookie集合的重新解析,@RestCookie直接绑定初始解析的Cookie集合,因此只能读取用户主动携带的Cookie。

2. 注解设计定位不同

  • @CookieParam是JAX-RS标准注解,在Resteasy Reactive中为了性能优化,参数绑定逻辑复用初始解析的Cookie缓存,同样无法读取过滤器修改后的请求头Cookie。
  • @RestCookie是Resteasy Reactive适配Vert.x模型新增的注解,专门用于快速读取初始解析的Cookie数据,不支持读取后续修改的请求头内容。

二、无需额外依赖的优化实现方案

方案1:利用请求上下文属性传递JWT,配合响应设置Cookie

这是适配Resteasy Reactive流程的稳妥方案:

步骤1:实现ServerRequestFilter和ServerResponseFilter

import jakarta.ws.rs.container.ContainerRequestContext
import jakarta.ws.rs.container.ContainerResponseContext
import jakarta.ws.rs.container.ServerRequestFilter
import jakarta.ws.rs.container.ServerResponseFilter
import jakarta.ws.rs.core.NewCookie
import jakarta.ws.rs.ext.Provider

@Provider
class JwtCookieUpserter : ServerRequestFilter, ServerResponseFilter {
    private val JWT_COOKIE_NAME = "auth-jwt"
    private val JWT_CONTEXT_KEY = "generated-jwt"

    override fun filter(requestContext: ContainerRequestContext) {
        // 检查是否存在目标Cookie
        val existingCookie = requestContext.cookies[JWT_COOKIE_NAME]
        if (existingCookie == null) {
            // 生成JWT(替换为你的实际生成逻辑)
            val jwtToken = generateJwtToken()
            // 将JWT存入请求上下文属性
            requestContext.setProperty(JWT_CONTEXT_KEY, jwtToken)
        }
    }

    override fun filter(requestContext: ContainerRequestContext, responseContext: ContainerResponseContext) {
        // 从上下文获取生成的JWT,设置响应Cookie
        val generatedJwt = requestContext.getProperty(JWT_CONTEXT_KEY) as? String
        generatedJwt?.let {
            val newCookie = NewCookie(
                JWT_COOKIE_NAME,
                it,
                "/",
                null,
                "JWT Auth Cookie",
                3600, // 1小时有效期
                false,
                true // 开启HttpOnly提升安全性
            )
            responseContext.headers.add("Set-Cookie", newCookie.toString())
        }
    }

    private fun generateJwtToken(): String {
        // 示例:替换为Quarkus JWT扩展的生成逻辑
        return "your-generated-jwt-token"
    }
}

步骤2:在ApiResource中获取JWT

import jakarta.ws.rs.GET
import jakarta.ws.rs.Path
import jakarta.ws.rs.core.Context
import jakarta.ws.rs.container.ContainerRequestContext

@Path("/api")
class ApiResource {
    private val JWT_CONTEXT_KEY = "generated-jwt"
    private val JWT_COOKIE_NAME = "auth-jwt"

    @GET
    @Path("/test")
    fun testEndpoint(@Context requestContext: ContainerRequestContext): String {
        // 优先取过滤器生成的JWT,再取请求Cookie
        val jwtToken = requestContext.getProperty(JWT_CONTEXT_KEY) as? String
            ?: requestContext.cookies[JWT_COOKIE_NAME]?.value
        return "Received JWT: $jwtToken"
    }
}

方案2:自定义CDI Producer简化JWT注入

通过CDI封装JWT获取逻辑,避免重复代码:

步骤1:创建自定义限定符注解

import jakarta.inject.Qualifier

@Qualifier
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
annotation class JwtCookie

步骤2:实现CDI Producer

import jakarta.enterprise.context.RequestScoped
import jakarta.enterprise.inject.Produces
import jakarta.inject.Inject
import jakarta.ws.rs.container.ContainerRequestContext

@RequestScoped
class JwtProducer {
    @Inject
    lateinit var requestContext: ContainerRequestContext

    private val JWT_COOKIE_NAME = "auth-jwt"
    private val JWT_CONTEXT_KEY = "generated-jwt"

    @Produces
    @JwtCookie
    fun produceJwt(): String? {
        return requestContext.getProperty(JWT_CONTEXT_KEY) as? String
            ?: requestContext.cookies[JWT_COOKIE_NAME]?.value
    }
}

步骤3:在资源中注入JWT

import jakarta.ws.rs.GET
import jakarta.ws.rs.Path
import jakarta.inject.Inject

@Path("/api")
class ApiResource {
    @Inject
    @JwtCookie
    lateinit var jwtToken: String?

    @GET
    @Path("/test")
    fun testEndpoint(): String {
        return "Received JWT: $jwtToken"
    }
}

方案3:直接修改ServerRequest的Cookie集合(适配Quarkus 2.13+)

若使用Quarkus 2.13及以上版本,可通过ServerRequest的mutate API直接修改Cookie集合,让@RestCookie能读取到过滤器添加的内容:

import jakarta.ws.rs.container.ServerRequestFilter
import jakarta.ws.rs.core.NewCookie
import jakarta.ws.rs.ext.Provider
import org.jboss.resteasy.reactive.server.ServerRequest

@Provider
class JwtCookieUpserter : ServerRequestFilter {
    private val JWT_COOKIE_NAME = "auth-jwt"

    override fun filter(request: ServerRequest) {
        val existingCookie = request.cookies()[JWT_COOKIE_NAME]
        if (existingCookie == null) {
            val jwtToken = generateJwtToken()
            // 更新请求的Cookie集合
            val updatedCookies = request.cookies().toMutableMap()
            updatedCookies[JWT_COOKIE_NAME] = NewCookie(JWT_COOKIE_NAME, jwtToken)
            // 返回修改后的请求
            return request.mutate().cookies(updatedCookies).build()
        }
    }

    private fun generateJwtToken(): String {
        return "your-generated-jwt-token"
    }
}

注意:仍需配合响应过滤器设置Set-Cookie头,确保客户端保存Cookie。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 18:05:37