Java JAX-RS访问控制允许原始标头不存在,但已添加到ResponseFilter中
我正试图连接到使用JAX-RS构建的REST端点,但得到的响应是,“Access Control Allow Origin”标题不存在。正在尝试使用Angular2获取资源 我尝试过的 我尝试了中提出的解决方案 我的响应过滤器: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
@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>