为何@RestCookie无法检测resteasy-reactive中ContainerRequestFilter设置的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
相关产品推荐
相关产品推荐

