[英]How to handle CORS using JAX-RS with Jersey
我正在開發一個 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();
}
截至目前,我收到錯誤請求的資源上不存在“Access-Control-Allow-Origin”標頭。 因此,不允許訪問 Origin ' http://localhost:8080 '。”
請幫我解決這個問題。
感謝和問候 佛陀普尼
注意:請務必閱讀底部的更新。 原始答案包括 CORS 過濾器的“惰性”實現
使用 Jersey 來處理 CORS,您可以只使用ContainerResponseFilter
。 Jersey 1.x 和 2.x 的ContainerResponseFilter
有點不同。 由於您還沒有提到您使用的是哪個版本,我將同時發布兩個版本。 確保使用正確的。
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",
"CSRF-Token, X-Requested-By, Authorization, Content-Type");
response.getHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHeaders().add("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS, HEAD");
}
}
如果您使用包掃描來發現提供者和資源, @Provider
注釋應該為您處理配置。 如果沒有,那么您將需要使用ResourceConfig
或Application
子類顯式注冊它。
使用ResourceConfig
顯式注冊過濾器的示例代碼:
final ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.register(new CORSFilter());
final final URI uri = ...;
final HttpServer httpServer = GrizzlyHttpServerFactory.createHttpServer(uri, resourceConfig);
對於 Jersey 2.x,如果您在注冊此過濾器時遇到問題,這里有一些資源可能會有所幫助
import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerResponse;
import com.sun.jersey.spi.container.ContainerResponseFilter;
@Provider
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",
"CSRF-Token, X-Requested-By, Authorization, Content-Type");
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 配置,可以使用
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
<param-value>com.yourpackage.CORSFilter</param-value>
</init-param>
或者ResourceConfig
你可以做
resourceConfig.getContainerResponseFilters().add(new CORSFilter());
或者使用@Provider
注釋進行包掃描。
請注意,上面的例子可以改進。 您將需要更多地了解 CORS 的工作原理。 請看這里。 一方面,您將獲得所有響應的標題。 這可能是不可取的。 您可能只需要處理預檢(或選項)。 如果你想看到一個更好的 CORS 過濾器,你可以查看RESTeasy CorsFilter
的源代碼
所以我決定添加一個更正確的實現。 上面的實現是惰性的,並將所有 CORS 標頭添加到所有請求中。 另一個錯誤是它只是一個響應過濾器,請求仍在處理。 這意味着當 preflight 請求進來時,它是一個 OPTIONS 請求,不會有 OPTIONS 方法實現,所以我們會得到 405 響應,這是不正確的。
這是它應該如何工作。 所以有兩種類型的 CORS 請求:簡單請求和預檢請求。 對於簡單的請求,瀏覽器將發送實際請求並添加Origin
請求標頭。 瀏覽器期望響應具有Access-Control-Allow-Origin
標頭,表示Access-Control-Allow-Origin
來自Origin
標頭的Origin
。 為了使其被視為“簡單請求”,它必須滿足以下標准:
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
如果請求不滿足所有這三個標准,則會發出預檢請求。 這是在發出實際請求之前向服務器發出的 OPTIONS 請求。 它將包含不同的Access-Control-XX-XX
標頭,服務器應使用自己的 CORS 響應標頭響應這些標頭。 以下是匹配的標頭:
請求頭 | 響應頭 |
---|---|
起源 | 訪問控制允許來源 |
訪問控制請求頭 | 訪問控制允許標題 |
訪問控制請求方法 | 訪問控制允許方法 |
XHR.withCredentials | 訪問控制允許憑據 |
對於Origin
請求標頭,該值將是源服務器域,並且響應Access-Control-Allow-Origin
應該是相同的地址或*
以指定允許所有源。
如果客戶端嘗試手動設置不在上述列表中的任何標頭,則瀏覽器將設置Access-Control-Request-Headers
標頭,其值為客戶端嘗試設置的所有標頭的列表。 服務器應該使用Access-Control-Allow-Headers
響應頭進行響應,其值是它允許的頭列表。
瀏覽器也會設置Access-Control-Request-Method
請求頭,值為請求的 HTTP 方法。 服務器應該使用Access-Control-Allow-Methods
響應頭進行響應,其值是它允許的方法列表。
如果客戶端使用XHR.withCredentials
,則服務器應使用Access-Control-Allow-Credentials
響應標頭進行響應,值為true
。 在這里閱讀更多。
綜上所述,這里有一個更好的實現。 盡管這比上面的實現更好,但它仍然不如我鏈接到的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", "*");
}
}
要了解有關 CORS 的更多信息,我建議閱讀有關跨域資源共享 (CORS)的 MDN 文檔
刪除注釋“ @CrossOriginResourceSharing(allowAllOrigins = true)
”
然后返回響應如下:
return Response.ok()
.entity(jsonResponse)
.header("Access-Control-Allow-Origin", "*")
.build();
但是jsonResponse
應該替換為 POJO 對象!
另一個答案可能嚴格正確,但具有誤導性。 缺少的部分是您可以將來自不同來源的過濾器混合在一起。 即使認為 Jersey 可能不提供 CORS 過濾器(這不是我檢查過的事實,但我相信其他答案),您可以使用tomcat 自己的 CORS 過濾器。
我在澤西島成功地使用了它。 例如,我有自己的基本身份驗證過濾器實現,以及 CORS。 最重要的是,CORS 過濾器是在 Web XML 中配置的,而不是在代碼中。
為了為我的項目解決這個問題,我使用了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>
CORS 過濾器是來自tomcat 站點的基本示例過濾器。
編輯:
maven.tomcat.web-xml.file變量是項目的 pom 定義屬性,它包含 web.xml 文件的路徑(位於我的項目中)
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.
因此,我沒有使用 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;
}
}
並將此類添加到 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>
使用JAX-RS,您可以簡單地將注釋@CrossOrigin(origin = yourURL)
到資源控制器。 您的情況是@CrossOrigin(origin = "http://localhost:8080")
但是您也可以使用@CrossOrigin(origin = "*")
來允許任何請求通過您的Web服務。
您可以檢查此以獲取更多信息。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.