Programing

Jersey에서 JAX-RS를 사용하여 CORS를 처리하는 방법

lottogame 2020. 12. 7. 07:42
반응형

Jersey에서 JAX-RS를 사용하여 CORS를 처리하는 방법


나는 JERSEY와 함께 JAX-RS로 작성한 모든 서비스 인 CORS를 처리 해야하는 서버 측에서 Java 스크립트 클라이언트 응용 프로그램을 개발 중입니다. 내 코드 :

@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'헤더가 없습니다.라는 오류가 발생합니다. 따라서 원본 ' http : // localhost : 8080 '은 액세스가 허용되지 않습니다.”

저를 도와주세요.

감사합니다 & 감사합니다 Buddha Puneeth


참고 : 하단의 업데이트를 읽으십시오.

@CrossOriginResourceSharing CXF 주석이므로 Jersey에서는 작동하지 않습니다.

Jersey에서는 CORS를 처리하기 위해 일반적으로 ContainerResponseFilter. ContainerResponseFilter저지 1과 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");
    }
}

패키지 검색을 사용하여 공급자 및 리소스를 검색하는 경우 @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의 경우이 필터를 등록하는 데 문제가있는 경우 도움이 될 수있는 몇 가지 리소스가 있습니다.

저지 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 구성, 사용할 수 있습니다

<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 헤더를 추가합니다. 또 다른 실수는 응답 필터 일뿐 요청이 여전히 프로세스라는 것입니다. 즉, OPTIONS 요청 인 프리 플라이트 요청이 들어 오면 OPTIONS 메서드가 구현되지 않으므로 405 응답을 받게되는데 이는 잘못된 것입니다.

작동 방식 다음과 같습니다 . 따라서 CORS 요청에는 단순 요청과 실행 전 요청 의 두 가지 유형이 있습니다. 간단한 요청의 경우 브라우저는 실제 요청을 보내고 Origin요청 헤더를 추가합니다 . 브라우저는 응답에 Access-Control-Allow-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

요청이이 세 가지 기준을 모두 충족하지 않으면 Preflight 요청이 이루어집니다. 이것은 실제 요청이 작성 되기 전에 서버에 작성되는 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    |
+-----------------------------------+--------------------------------------+
  • 으로 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 better than the above implementation, it is still inferior to the RESTEasy one I 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.

@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");
        }

        // 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)


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.

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.


Remove annotation "@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!


To solve this for my project I used Micheal's answer and arrived at this:

    <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.file variable is a pom defined property for the project and it contains the path to the web.xml file (located within my project)


peeskillet's answer is correct. But I get this error when refresh the web page (it is working only on first load):

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

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

<init-param>
            <param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
            <param-value>com.yourpackage.MCORSFilter</param-value>
        </init-param>

Using JAX-RS you could simply add the annotation @CrossOrigin(origin = yourURL) to your resource controller. In your case would be @CrossOrigin(origin = "http://localhost:8080") but you could also use @CrossOrigin(origin = "*") to allow any request to get through your webservice.
You could check THIS for more information.

참고URL : https://stackoverflow.com/questions/28065963/how-to-handle-cors-using-jax-rs-with-jersey

반응형