CORSFilter不适用于JAX-RS应用程序

时间:2017-05-26 19:11:14

标签: java rest jax-rs wildfly

我正在使用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 Headers发回。 我在这里缺少什么?

2 个答案:

答案 0 :(得分:4)

我猜你从this post得到了你的代码。这个Q&amp; A的要点是OP希望能够继续使用类路径扫描,即保持

@ApplicationPath("/api")
public class RestApplication extends Application {
}

当你有一个像这样的空Application类时,它会触发使用@Path@Provider注释的类的类路径扫描。所有这些课程都会自动注册。但是,一旦覆盖getClasses()类中的getSingletons()Application,并在其中任何一个中返回非空集,就会自动禁用类路径扫描。

所以OP试图找出如何在不禁用类路径扫描的情况下注册CorsFilter。解决方案是使用Feature注释@Provider。 <{1}}允许@Provider自动发现并注册。

在您的情况下,您没有禁用类路径扫描。所以你只需要注册Feature或者因为你没有使用类路径扫描,你可以忘记Feature,只需在Feature中直接注册CorsFilter即可。类

Application

顺便说一句,即使您已正确注册过滤器,您的cURL请求也不会显示CORS标头。期望在请求中看到@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; } 标题。

更新

另外,您需要考虑有关servlet过滤器和JAX-RS应用程序的调用顺序。在JAX-RS之前调用servlet过滤器。当涉及到JAX-RS级别的CORS支持时,这会产生影响。

那么将要发生的是当客户端发出预检(CORS)请求时,将调用servlet过滤器。请注意,这只是一个预检请求,因此不会发送任何标头,包括令牌标头。预检只是检查服务器是否允许请求。此预检发生在真实请求之前。这就是CORS协议的工作原理。

所以在预检时,响应应该包括CORS响应头,但是对于你的servlet过滤器,它不会发送它。您只是发送未经授权的回复。所以CORS永远不会起作用。

一些解决方案是:

  1. 在servlet过滤器级别设置CORS支持。您将无法使用RESTEasy Origin。您只需要自己实现它。

  2. 您可以改为使用JAX-RS ContainerRequestFilter而不是在servlet过滤器中执行身份验证。这样,auth和CORS支持处于同一级别。

  3. 如果你使用第二个选项,你基本上会做类似

    的事情
    CorsFilter

答案 1 :(得分:0)

您是否调试并检查请求是否通过您的过滤器?我不确定将它作为一个功能实现是否可行但是从我能找到的内容来看,大多数资源都建议实现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://stackoverflow.com/a/23631475/1849366

http://www.baeldung.com/cors-in-jax-rs