CORSFilter 不适用于 JAX-RS 应用程序
Posted
技术标签:
【中文标题】CORSFilter 不适用于 JAX-RS 应用程序【英文标题】:CORSFilter is not applied on JAX-RS Application 【发布时间】:2017-10-27 18:23:24 【问题描述】:我正在使用resteasy-jaxrs - 3.0.9.FINAL
。
我有两个单独的javax.ws.rs.core.Application
s
@ApplicationPath("oauth")
public class OAuthApplication extends Application
final Set<Class<?>> classes = new HashSet<>();
@Nonnull
@Override
public Set<Class<?>> getClasses()
classes.add(RegisterResource.class);
classes.add(TokenResource.class);
classes.add(HelloResource.class);
return Collections.unmodifiableSet(classes);
对于基于/oauth
的端点和
@ApplicationPath("rest")
public class RestApplication extends Application
final Set<Class<?>> classes = new HashSet<>();
@Nonnull
@Override
public Set<Class<?>> getClasses()
classes.add(CategoryResource.class);
classes.add(CategoryGroupResource.class);
classes.add(TransactionResource.class);
classes.add(MonthlySummaryResource.class);
classes.add(MemberResource.class);
return Collections.unmodifiableSet(classes);
我的过滤器看起来像
@Provider
public class CorsFeature implements Feature
@Override
public boolean configure(FeatureContext featureContext)
CorsFilter corsFilter = new CorsFilter();
corsFilter.getAllowedOrigins().add("*");
corsFilter.setAllowedMethods("OPTIONS, GET, POST, DELETE, PUT, PATCH");
featureContext.register(corsFilter);
return true;
我还对我的/rest/*
资源应用过滤器,看起来像
@WebFilter("/rest/*")
public class AuthTokenValidatorFilter implements Filter
private static final String BEARER_HEADER = "BEARER";
private static final String COLON = ":";
private static final Pattern PATTERN = Pattern.compile(COLON);
@Override
public void init(final FilterConfig filterConfig) throws ServletException
@Override
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException
final HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
final HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
if (httpRequest.getHeader(BEARER_HEADER) == null || !isValidAuthToken(httpRequest.getHeader(BEARER_HEADER)))
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
return;
filterChain.doFilter(servletRequest, servletResponse);
private static boolean isValidAuthToken(@Nonnull final String header)
final String[] tokenParts = PATTERN.split(header);
if (tokenParts.length != 3)
// hash, uuid, timestamp
return false;
final int nanoTimeStamp;
try
nanoTimeStamp = Integer.parseInt(tokenParts[2]);
catch (final NumberFormatException e)
return false;
final String hashedAuthToken = new UniqueIdGenerator().getHashedAuthToken(tokenParts[1], nanoTimeStamp);
return hashedAuthToken.equals(tokenParts[0]);
@Override
public void destroy()
当我启动应用程序并点击端点时,我看到了
~ curl -v http://localhost:9090/application/oauth/hello
* Trying ::1...
* Connected to localhost (::1) port 9090 (#0)
> GET /application/oauth/hello HTTP/1.1
> Host: localhost:9090
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Connection: keep-alive
< X-Powered-By: Undertow 1
< Server: Wildfly 8
< Content-Type: application/octet-stream
< Content-Length: 5
< Date: Fri, 26 May 2017 19:05:15 GMT
<
* Connection #0 to host localhost left intact
hello%
我没有看到发回的 CORS 标头。 我在这里错过了什么?
【问题讨论】:
【参考方案1】:我/我猜你是从this post 得到你的代码的。这个问答的重点是 OP 希望能够继续使用类路径扫描,即只保留
@ApplicationPath("/api")
public class RestApplication extends Application
当您有一个像这样的空 Application
类时,它会触发类路径扫描以查找带有 @Path
和 @Provider
注释的类。所有这些类都是自动注册的。但是,一旦您在 Application
类中覆盖 getClasses()
或 getSingletons()
,并在其中任何一个中返回一个非空集,类路径扫描就会自动禁用。
所以 OP 试图找出他们如何在不禁用类路径扫描的情况下注册CorsFilter
。解决方案是使用带有@Provider
注释的Feature
。 @Provider
允许自动发现和注册Feature
。
在你的情况下,你没有类路径扫描,因为你禁用了它。所以你只需要注册Feature
,或者因为你没有使用类路径扫描,你可以忘记Feature
,直接在Application
类中注册CorsFilter
@Override
public Set<Object> getSingletons()
Set<Object> providers = new HashSet<>();
CorsFilter corsFilter = new CorsFilter();
corsFilter.getAllowedOrigins().add("*");
corsFilter.setAllowedMethods("OPTIONS, GET, POST, DELETE, PUT, PATCH");
providers.add(corsFilter);
return providers;
顺便说一句,即使您正确注册了过滤器,您的 cURL 请求也不会显示 CORS 标头。它期望在请求中看到 Origin
标头。
更新
另外,您需要考虑关于 servlet 过滤器和 JAX-RS 应用程序的调用顺序。 servlet 过滤器在 JAX-RS 之前调用。当涉及到 JAX-RS 级别的 CORS 支持时,这会产生影响。
所以将会发生的是,当客户端发出预检 (CORS) 请求时,将调用 servlet 过滤器。请注意,这只是一个预检请求,因此不会发送任何标头,包括令牌标头。预检只是检查服务器是否允许请求。此预检发生在实际请求之前。这就是 CORS 协议的工作原理。
因此,在预检时,响应应该包含 CORS 响应标头,但使用您的 servlet 过滤器时,它不会发送此信息。您只是发送未经授权的响应。所以 CORS 永远不会起作用。
几个解决方案是:
在 servlet 过滤器级别设置 CORS 支持。您将无法使用 RESTEasy CorsFilter
。你只需要自己实现它。
您可以使用JAX-RS ContainerRequestFilter,而不是在 servlet 过滤器中执行身份验证。这样身份验证和 CORS 支持处于同一级别。
如果你选择第二个选项,你基本上会做类似的事情
if (notAuthenticated())
requestContext.abortWith(Response.status(401).build()));
return;
【讨论】:
伙计,你救了我的命,如果你愿意,你应该为 3.1.x resteasy 更新对此的引用,因为我看到的所有其他 3.0.9 实现在此之后都崩溃了。【参考方案2】:您是否调试并检查请求是否通过您的过滤器?我不确定将其作为功能实现是否可行,但据我所知,大多数资源都建议按如下方式实现 CORS 过滤器。
@Provider
public class CorsFilter implements ContainerResponseFilter
@Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException
responseContext.getHeaders().add("Access-Control-Allow-Origin", "*");
responseContext.getHeaders().add("Access-Control-Allow-Credentials", "true");
responseContext.getHeaders().add("Access-Control-Allow-Headers","origin, content-type, accept, authorization");
responseContext.getHeaders().add("Access-Control-Allow-Methods","GET, POST, PUT, DELETE, OPTIONS, HEAD");
参考文献:
https://***.com/a/23631475/1849366
http://www.baeldung.com/cors-in-jax-rs
【讨论】:
是的,我试过了,但即使在那种情况下,电话也没有通过ContainerResponseFilter
。以上是关于CORSFilter 不适用于 JAX-RS 应用程序的主要内容,如果未能解决你的问题,请参考以下文章
RESTful API / MVC 应用程序的 Grails 与 JAX-RS
在用于 OSGi 的 JAX-RS 白板的参考实现中,啥调用 createWhiteboard(..)?