Java JAX-RS访问控制允许原始标头不存在,但已添加到ResponseFilter中

Java JAX-RS访问控制允许原始标头不存在,但已添加到ResponseFilter中,java,rest,angular,jersey,jax-rs,Java,Rest,Angular,Jersey,Jax Rs,我正试图连接到使用JAX-RS构建的REST端点,但得到的响应是,“Access Control Allow Origin”标题不存在。正在尝试使用Angular2获取资源 我尝试过的 我尝试了中提出的解决方案 我的响应过滤器: @Provider @PreMatching public class ResponseFilter implements ContainerResponseFilter { @Override public void filter(Container

我正试图连接到使用JAX-RS构建的REST端点,但得到的响应是,“Access Control Allow Origin”标题不存在。正在尝试使用Angular2获取资源

我尝试过的

我尝试了中提出的解决方案

我的响应过滤器:

@Provider
@PreMatching
public class ResponseFilter 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-Headers", "origin, content-type, accept, authorization");
        responseContext.getHeaders().add("Access-Control-Allow-Credentials", "true");
        responseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
    }
}
和my web.xml servlet映射:

    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.classnames</param-name>
            <param-value>com.api.ResponseFilter</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

但我还是犯了同样的错误。 在postman中测试时,我得到了正确的标题:

Access-Control-Allow-Credentials →true
Access-Control-Allow-Headers →origin, content-type, accept, authorization
Access-Control-Allow-Methods →GET, POST, PUT, DELETE, OPTIONS, HEAD
Access-Control-Allow-Origin →*
更新2

在扩展应用程序的JAXRSConfig中,我手动添加了所有可能需要的资源:

@ApplicationPath("api")
public class JAXRSConfig extends Application {

    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> resources = new HashSet<>();
        addRestResourceClasses(resources);
        return resources;
    }

    private void addRestResourceClasses(Set<Class<?>> resources) {
        resources.add(AdminResource.class);
        resources.add(RequestFilter.class);
        resources.add(ResponseFilter.class);
        resources.add(TweetResource.class);
        resources.add(UserResource.class);
    }
}
更新3

我确实意识到我实际上并没有将定义的头添加到请求中;因此,我已将角度代码更新为:

let authToken = "Basic " + btoa("henk:asdf");//temporary until I learn how to allow a user to log in, in angular
    let headers = new Headers();
    headers.append('Content-Type', 'application/json');
    headers.append('Authorization', authToken);

    //let options = new RequestOptions({ headers: headers});

    return this.http.get(this.myUrl, {headers: headers})
      .map((res:Response) => res.json())
      .catch((error:any) => Observable.throw(error.json().error || 'Server error'));
更新4

我了解到这可能是由于Chrome(和其他浏览器)通过名为OPTIONS的请求方法执行飞行前检查。我在api中添加了一个请求过滤器,如下所示:

@Provider
@PreMatching
public class RequestFilter implements ContainerRequestFilter {
    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        if (requestContext.getRequest().getMethod().equals("OPTIONS")) {
            requestContext.abortWith(Response.status(Response.Status.OK).header("Access-Control-Allow-Origin", "http://localhost:4200").build());
        }
    }
}
但是没有用。在if语句上有一个断点,我看到这个方法不会在angular的get请求中被调用,但它会在Postman的请求中被调用

我也尝试了一个不同的CORS过滤器根据。 但这也不起作用

更新5

根据,我尝试创建一个实现javax.servlet.Filter的类,以将所需的头添加到响应中:

public class ContainerResponseFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse res = (HttpServletResponse) response;
        res.addHeader("Access-Control-Allow-Origin", "http://localhost:4200");
        res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
        res.addHeader("Access-Control-Allow-Headers", "Content-Type");
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {

    }
}
使用my web.xml中的对应映射:

    <filter>
        <filter-name>corsfilter</filter-name>
        <filter-class>com.api.ContainerResponseFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>corsfilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

克斯菲尔特
com.api.ContainerResponseFilter
克斯菲尔特
/*
我在应用程序配置中添加了ContainerResponseFilter作为资源。
不幸的是,我仍然收到“Access Control Allow Origin”标头不存在的错误。

事实证明,问题在于身份验证。我的应用程序服务器正在尝试对请求进行身份验证,但由于在进行身份验证之前进行了CORS检查,因此我得到了一个未经授权的响应,其中没有包含所需的标头。

我发现您在请求中使用的是基本身份验证。您使用的是哪种类型的安全性(体系结构)?如果您使用的是某种servlet级别的过滤器(比如Sprig安全性),则不会调用Jersey过滤器,因为在到达Jersey之前,始终会调用servlet级别的过滤器。如果您有这种安全体系结构,然后在该级别配置CORS,而不是在Jersey级别配置CORS。我使用JAAS进行基本身份验证设置,这将检查jdbc领域中的用户名和密码组合。我已经在我的应用服务器(Payara)中配置了这个。对于基本身份验证,GET请求需要一个授权头和一个用户名和密码的base64编码字符串,这就是更新3下面的内容。正如您在更新4的底部所看到的,我已经尝试在我的web.xml中使用servlet映射实现CORS过滤器,但这也不起作用。删除服务器的任何身份验证。如果请求有效,那么我认为这就是问题所在:请求流是Auth->Filter->Jersey。身份验证是在到达任何筛选器之前在服务器上执行的操作。这是一个问题,因为不是获取CORS头,而是获取未经验证的消息。如果是这样的话,那么我不确定您如何实现这一点。这是需要在请求的早期阶段进行配置的内容,这将特定于您的服务器。我注意到的另一件事是,您从新筛选器中允许的标题中删除了
授权
。非常感谢您在@peeskillet提供的扩展帮助!事实证明,身份验证确实是问题所在,在删除之后,我的请求会顺利通过。
@Provider
@PreMatching
public class RequestFilter implements ContainerRequestFilter {
    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        if (requestContext.getRequest().getMethod().equals("OPTIONS")) {
            requestContext.abortWith(Response.status(Response.Status.OK).header("Access-Control-Allow-Origin", "http://localhost:4200").build());
        }
    }
}
public class ContainerResponseFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse res = (HttpServletResponse) response;
        res.addHeader("Access-Control-Allow-Origin", "http://localhost:4200");
        res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
        res.addHeader("Access-Control-Allow-Headers", "Content-Type");
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {

    }
}
    <filter>
        <filter-name>corsfilter</filter-name>
        <filter-class>com.api.ContainerResponseFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>corsfilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>