Java 如何使用 JAX-RS 和 Jersey 处理 CORS
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28065963/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to handle CORS using JAX-RS with Jersey
提问by user2773716
I'm developing a java script client application, in server-side I need to handle CORS, all the services I had written in JAX-RS with JERSEY. My code:
我正在开发一个 java 脚本客户端应用程序,在服务器端我需要处理 CORS,我用 JERSEY 在 JAX-RS 中编写的所有服务。我的代码:
@CrossOriginResourceSharing(allowAllOrigins = true)
@GET
@Path("/readOthersCalendar")
@Produces("application/json")
public Response readOthersCalendar(String dataJson) throws Exception {
//my code. Edited by gimbal2 to fix formatting
return Response.status(status).entity(jsonResponse).header("Access-Control-Allow-Origin", "*").build();
}
As of now, i'm getting error No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access.”
截至目前,我收到错误请求的资源上不存在“Access-Control-Allow-Origin”标头。因此,不允许访问Origin ' http://localhost:8080'。”
Please assist me with this.
请帮我解决这个问题。
Thanks & Regards Buddha Puneeth
感谢和问候 佛陀普尼
回答by Paul Samsotha
Note: Make sure to read the UPDATE at the bottom
注意:请务必阅读底部的更新
@CrossOriginResourceSharing
is a CXF annotation, so it won't work with Jersey.
@CrossOriginResourceSharing
是 CXF 注释,因此它不适用于 Jersey。
With Jersey, to handle CORS, I normally just use a ContainerResponseFilter
. The ContainerResponseFilter
for Jersey 1 and 2 are a bit different. Since you haven't mentioned which version you're using, I'll post both.
对于 Jersey,为了处理 CORS,我通常只使用ContainerResponseFilter
. 所述ContainerResponseFilter
用于泽西1和2是有点不同。由于您还没有提到您使用的是哪个版本,我将同时发布两个版本。
Jersey 2
泽西岛 2
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
@Provider
public class CORSFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext request,
ContainerResponseContext response) throws IOException {
response.getHeaders().add("Access-Control-Allow-Origin", "*");
response.getHeaders().add("Access-Control-Allow-Headers",
"origin, content-type, accept, authorization");
response.getHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHeaders().add("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS, HEAD");
}
}
If you use package scanning to discover providers and resources, the @Provider
annotation should take care of the configuration for you. If not, then you will need to explicitly register it with the ResourceConfig
or the Application
subclass.
如果您使用包扫描来发现提供者和资源,则@Provider
注释应该为您处理配置。如果没有,那么您将需要向ResourceConfig
或Application
子类显式注册它。
Sample code to explicitly register filter with the ResourceConfig
:
显式注册过滤器的示例代码ResourceConfig
:
final ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.register(new CORSFilter());
final final URI uri = ...;
final HttpServer httpServer = GrizzlyHttpServerFactory.createHttpServer(uri, resourceConfig);
For Jersey 2.x, if you are having problems registering this filter, here are a couple resources that might help
对于 Jersey 2.x,如果您在注册此过滤器时遇到问题,这里有一些资源可能会有所帮助
Jersey 1
泽西岛 1
import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerResponse;
import com.sun.jersey.spi.container.ContainerResponseFilter;
public class CORSFilter implements ContainerResponseFilter {
@Override
public ContainerResponse filter(ContainerRequest request,
ContainerResponse response) {
response.getHttpHeaders().add("Access-Control-Allow-Origin", "*");
response.getHttpHeaders().add("Access-Control-Allow-Headers",
"origin, content-type, accept, authorization");
response.getHttpHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHttpHeaders().add("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS, HEAD");
return response;
}
}
web.xml configuration, you can use
web.xml 配置,可以使用
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
<param-value>com.yourpackage.CORSFilter</param-value>
</init-param>
Or ResourceConfig
you can do
或者ResourceConfig
你可以做
resourceConfig.getContainerResponseFilters().add(new CORSFilter());
Or package scanning with the @Provider
annotation.
或者带@Provider
注释的包扫描。
EDIT
编辑
Please note that the above example can be improved. You will need to know more about how CORS works. Please see here. For one, you will get the headers for all responses. This may not be desirable. You may just need to handle the preflight (or OPTIONS). If you want to see a better implemented CORS filter, you can check out the source code for the RESTeasy CorsFilter
请注意,上面的例子可以改进。您将需要更多地了解 CORS 的工作原理。请看这里。一方面,您将获得所有响应的标题。这可能是不可取的。您可能只需要处理预检(或选项)。如果你想看到一个更好实现的 CORS 过滤器,你可以查看RESTeasy的源代码CorsFilter
UPDATE
更新
So I decided to add a more correct implementation. The above implementation is lazy and adds all the CORS headers to all requests. The other mistake is that being that it is only a responsefilter, the request is still processes. This means that when the preflight request comes in, which is an OPTIONS request, there will be no OPTIONS method implemented, so we will get a 405 response, which is incorrect.
所以我决定添加一个更正确的实现。上面的实现是惰性的,并将所有 CORS 标头添加到所有请求中。另一个错误是它只是一个响应过滤器,请求仍在处理。这意味着当 preflight 请求进来时,它是一个 OPTIONS 请求,不会有 OPTIONS 方法实现,所以我们会得到 405 响应,这是不正确的。
Here's how it shouldwork. So there are two types of CORS requests: simple requests and preflight requests. For a simple request, the browser will send the actual request and add the Origin
request header. The browser expects for the response to have the Access-Control-Allow-Origin
header, saying that the origin from the Origin
header is allowed. In order for it to be considered a "simple request", it must meet the following criteria:
这是它应该如何工作。所以有两种类型的 CORS 请求:简单请求和预检请求。对于简单的请求,浏览器将发送实际请求并添加Origin
请求头。浏览器期望响应具有Access-Control-Allow-Origin
标头,表示Origin
允许来自标头的来源。为了使其被视为“简单请求”,它必须满足以下标准:
- Be one of the following method:
- GET
- HEAD
- POST
- Apart from headers automatically set by the browser, the request may only contain the following manuallyset headers:
Accept
Accept-Language
Content-Language
Content-Type
DPR
Save-Data
Viewport-Width
Width
- The only allowed values for the
Content-Type
header are:application/x-www-form-urlencoded
multipart/form-data
text/plain
- 是以下方法之一:
- 得到
- 头
- 邮政
- 除了浏览器自动设置的标头外,请求可能只包含以下手动设置的标头:
Accept
Accept-Language
Content-Language
Content-Type
DPR
Save-Data
Viewport-Width
Width
Content-Type
标头的唯一允许值是:application/x-www-form-urlencoded
multipart/form-data
text/plain
If the request doesn't meet all of these three criteria, a Preflight request is made. This is an OPTIONS request that is made to the server, priorto the actual request being made. It will contain different Access-Control-XX-XX
headers, and the server should respond to those headers with its own CORS response headers. Here are the matching headers:
如果请求不满足所有这三个标准,则会发出预检请求。这是在发出实际请求之前向服务器发出的 OPTIONS 请求。它将包含不同的Access-Control-XX-XX
标头,服务器应使用自己的 CORS 响应标头响应这些标头。以下是匹配的标头:
Preflight Request and Response Headers
+-----------------------------------+--------------------------------------+
| REQUEST HEADER | RESPONSE HEADER |
+===================================+======================================+
| Origin | Access-Control-Allow-Origin |
+-----------------------------------+--------------------------------------+
| Access-Control-Request-Headers | Access-Control-Allow-Headers |
+-----------------------------------+--------------------------------------+
| Access-Control-Request-Method | Access-Control-Allow-Methods |
+-----------------------------------+--------------------------------------+
| XHR.withCredentials | Access-Control-Allow-Credentials |
+-----------------------------------+--------------------------------------+
With the
Origin
request header, the value will be the origin server domain, and the responseAccess-Control-Allow-Header
should be either this same address or*
to specify that all origins are allowed.If the client tries to manually set any headers not in the above list, then the browser will set the
Access-Control-Request-Headers
header, with the value being a list of all the headers the client is trying to set. The server should respond back with aAccess-Control-Allow-Headers
response header, with the value being a list of headers it allows.The browser will also set the
Access-Control-Request-Method
request header, with the value being the HTTP method of the request. The server should respond with theAccess-Control-Allow-Methods
response header, with the value being a list of the methods it allows.If the client uses the
XHR.withCredentials
, then the server should respond with theAccess-Control-Allow-Credentials
response header, with a value oftrue
. Read more here.
对于
Origin
请求标头,该值将是源服务器域,并且响应Access-Control-Allow-Header
应该是相同的地址或*
指定允许所有源。如果客户端尝试手动设置不在上述列表中的任何标头,则浏览器将设置
Access-Control-Request-Headers
标头,其值为客户端尝试设置的所有标头的列表。服务器应该用一个Access-Control-Allow-Headers
响应头来响应,其值是它允许的头列表。浏览器也会设置
Access-Control-Request-Method
请求头,其值为请求的 HTTP 方法。服务器应该使用Access-Control-Allow-Methods
响应头进行响应,其值是它允许的方法列表。如果客户端使用
XHR.withCredentials
,则服务器应使用Access-Control-Allow-Credentials
响应标头进行响应,其值为true
。在这里阅读更多。
So with all that said, here is a better implementation. Even though this is betterthan the above implementation, it is still inferior to the RESTEasy oneI linked to, as this implementation still allows all origins. But this filter does a better job of adhering to the CORS spec than the above filter which just adds the CORS response headers to all request. Note that you may also need to modify the Access-Control-Allow-Headers
to match the headers that your application will allow; you may want o either add or remove some headers from the list in this example.
综上所述,这里有一个更好的实现。尽管这比上面的实现更好,但它仍然不如我链接到的RESTEasy,因为这个实现仍然允许所有来源。但是这个过滤器在遵守 CORS 规范方面做得比上面的过滤器更好,后者只是将 CORS 响应标头添加到所有请求中。请注意,您可能还需要修改Access-Control-Allow-Headers
以匹配您的应用程序允许的标头;在此示例中,您可能希望从列表中添加或删除一些标题。
@Provider
@PreMatching
public class CorsFilter implements ContainerRequestFilter, ContainerResponseFilter {
/**
* Method for ContainerRequestFilter.
*/
@Override
public void filter(ContainerRequestContext request) throws IOException {
// If it's a preflight request, we abort the request with
// a 200 status, and the CORS headers are added in the
// response filter method below.
if (isPreflightRequest(request)) {
request.abortWith(Response.ok().build());
return;
}
}
/**
* A preflight request is an OPTIONS request
* with an Origin header.
*/
private static boolean isPreflightRequest(ContainerRequestContext request) {
return request.getHeaderString("Origin") != null
&& request.getMethod().equalsIgnoreCase("OPTIONS");
}
/**
* Method for ContainerResponseFilter.
*/
@Override
public void filter(ContainerRequestContext request, ContainerResponseContext response)
throws IOException {
// if there is no Origin header, then it is not a
// cross origin request. We don't do anything.
if (request.getHeaderString("Origin") == null) {
return;
}
// If it is a preflight request, then we add all
// the CORS headers here.
if (isPreflightRequest(request)) {
response.getHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHeaders().add("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS, HEAD");
response.getHeaders().add("Access-Control-Allow-Headers",
// Whatever other non-standard/safe headers (see list above)
// you want the client to be able to send to the server,
// put it in this list. And remove the ones you don't want.
"X-Requested-With, Authorization, " +
"Accept-Version, Content-MD5, CSRF-Token, Content-Type");
}
// Cross origin requests can be either simple requests
// or preflight request. We need to add this header
// to both type of requests. Only preflight requests
// need the previously added headers.
response.getHeaders().add("Access-Control-Allow-Origin", "*");
}
}
To learn more about CORS, I suggest reading the MDN docs on Cross-Origin Resource Sharing (CORS)
要了解有关 CORS 的更多信息,我建议阅读有关跨域资源共享 (CORS)的 MDN 文档
回答by Michael
The other answer might be strictly correct, but misleading. The missing part is that you can mix filters from different sources together. Even thought Jersey might not provide CORS filter (not a fact I checked but I trust the other answer on that), you can use tomcat's own CORS filter.
另一个答案可能严格正确,但具有误导性。缺少的部分是您可以将来自不同来源的过滤器混合在一起。即使认为 Jersey 可能不提供 CORS 过滤器(这不是我检查过的事实,但我相信其他答案),您可以使用tomcat 自己的 CORS 过滤器。
I am using it successfully with Jersey. I have my own implementation of Basic Authentication filter, for example, together with CORS. Best of all, CORS filter is configured in web XML, not in code.
我在泽西岛成功地使用了它。例如,我有自己的基本身份验证过滤器实现,以及 CORS。最重要的是,CORS 过滤器是在 Web XML 中配置的,而不是在代码中。
回答by Dark Star1
To solve this for my project I used Micheal'sanswer and arrived at this:
为了为我的项目解决这个问题,我使用了Micheal 的答案并得出了以下结论:
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<executions>
<execution>
<id>run-embedded</id>
<goals>
<goal>run</goal>
</goals>
<phase>pre-integration-test</phase>
<configuration>
<port>${maven.tomcat.port}</port>
<useSeparateTomcatClassLoader>true</useSeparateTomcatClassLoader>
<contextFile>${project.basedir}/tomcat/context.xml</contextFile>
<!--enable CORS for development purposes only. The web.xml file specified is a copy of
the auto generated web.xml with the additional CORS filter added -->
<tomcatWebXml>${maven.tomcat.web-xml.file}</tomcatWebXml>
</configuration>
</execution>
</executions>
</plugin>
The CORS filter being the basic example filter from the tomcat site.
Edit:
The maven.tomcat.web-xml.filevariable is a pom defined property for the project and it contains the path to the web.xml file (located within my project)
CORS 过滤器是来自tomcat 站点的基本示例过滤器。
编辑:
该maven.tomcat.web-xml.file变量是该项目的POM定义的属性,它包含的路径web.xml文件(位于我的项目中)
回答by minhlong293
peeskillet's answer is correct. But I get this error when refresh the web page (it is working only on first load):
peeskillet 的回答是正确的。但是刷新网页时出现此错误(它仅在第一次加载时有效):
The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed. Origin 'http://127.0.0.1:8080' is therefore not allowed access.
So instead of using add method to add headers for response, I using put method. This is my class
因此,我没有使用 add 方法为响应添加标头,而是使用 put 方法。这是我的课
public class MCORSFilter implements ContainerResponseFilter {
public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
public static final String ACCESS_CONTROL_ALLOW_ORIGIN_VALUE = "*";
private static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
private static final String ACCESS_CONTROL_ALLOW_CREDENTIALS_VALUE = "true";
public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
public static final String ACCESS_CONTROL_ALLOW_HEADERS_VALUE = "Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With, Accept";
public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
public static final String ACCESS_CONTROL_ALLOW_METHODS_VALUE = "GET, POST, PUT, DELETE, OPTIONS, HEAD";
public static final String[] ALL_HEADERs = {
ACCESS_CONTROL_ALLOW_ORIGIN,
ACCESS_CONTROL_ALLOW_CREDENTIALS,
ACCESS_CONTROL_ALLOW_HEADERS,
ACCESS_CONTROL_ALLOW_METHODS
};
public static final String[] ALL_HEADER_VALUEs = {
ACCESS_CONTROL_ALLOW_ORIGIN_VALUE,
ACCESS_CONTROL_ALLOW_CREDENTIALS_VALUE,
ACCESS_CONTROL_ALLOW_HEADERS_VALUE,
ACCESS_CONTROL_ALLOW_METHODS_VALUE
};
@Override
public ContainerResponse filter(ContainerRequest request, ContainerResponse response) {
for (int i = 0; i < ALL_HEADERs.length; i++) {
ArrayList<Object> value = new ArrayList<>();
value.add(ALL_HEADER_VALUEs[i]);
response.getHttpHeaders().put(ALL_HEADERs[i], value); //using put method
}
return response;
}
}
And add this class to init-param in web.xml
并将此类添加到 web.xml 中的 init-param
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
<param-value>com.yourpackage.MCORSFilter</param-value>
</init-param>
回答by Aupr
Remove annotation "@CrossOriginResourceSharing(allowAllOrigins = true)
"
删除注释“ @CrossOriginResourceSharing(allowAllOrigins = true)
”
Then Return Response like below:
然后返回响应如下:
return Response.ok()
.entity(jsonResponse)
.header("Access-Control-Allow-Origin", "*")
.build();
But the jsonResponse
should replace with a POJO Object!
但是jsonResponse
应该替换为 POJO 对象!