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

我正在使用resteasy-jaxrs-3.0.9.FINAL.
我有两个单独的javax.ws.rs.core.Applications

@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标头.
我在这里想念什么?

解决方法:

我/您猜想您的代码是从this post开始的.此问与答的要点是OP希望能够继续使用类路径扫描,即保持

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

当您有一个空的Application类时,它将触发对用@Path和@Provider注释的类的类路径扫描.所有这些类都将自动注册.但是,一旦在Application类中重写getClasses()或getSingletons()并在其中一个中返回非空集,则将自动禁用类路径扫描.

因此,OP试图找出如何在不禁用类路径扫描的情况下注册CorsFilter.解决方案是使用带有@Provider注释的功能. @Provider允许自动发现和注册功能.

在您的情况下,您禁用了类路径扫描功能.因此,您只需要注册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;
}
上一篇:Java-禁用或取消部署我的应用程序后计划仍在运行


下一篇:SonarQube代码质量管理工具安装与使用(sonarqube5.1.2 + sonar-runner-dist-2.4 + MySQL5.x)