Bagaimana cara mengaktifkan permintaan lintas domain pada layanan web JAX-RS?

Saya mengembangkan serangkaian layanan web yang tenang. Saya tidak dapat memanggil metode ini dari klien jarak jauh karena kesalahan No 'Access-Control-Allow-Origin' header is present on the requested resource.

Layanan ini bekerja dengan sempurna di localhost. Apakah ada perubahan atau konfigurasi yang harus dilakukan pada sisi server untuk mengatasi masalah tersebut. yaitu untuk mengaktifkan permintaan lintas domain.

Saya menggunakan WildFly 8, JavaEE 7


person Naili    schedule 03.05.2014    source sumber
comment
Anda dapat mempelajari lebih lanjut tentang CORS di sini: html5rocks.com/en/tutorials/cors Anda dapat mempelajari cara menambahkan dukungan CORS ke JAX-RS di sini: cxf.apache. org/docs/jax-rs-cors.html   -  person monsur    schedule 04.05.2014
comment
Itu berguna, tapi saya sedang mencari solusi tanpa mengubah kode klien.   -  person Naili    schedule 04.05.2014
comment
Pertanyaan Anda meminta perubahan pada sisi server. Sebagian besar perubahan CORS dilakukan di sisi server. Hanya ada sedikit, jika ada, perubahan yang diperlukan pada klien.   -  person monsur    schedule 04.05.2014


Jawaban (9)


Saya menanyakan hal yang sama, jadi setelah sedikit riset saya menemukan bahwa cara termudah adalah dengan menggunakan JAX-RS ContainerResponseFilter untuk menambahkan header CORS yang relevan. Dengan cara ini Anda tidak perlu mengganti seluruh tumpukan layanan web dengan CXF (Wildfly menggunakan CXF dalam beberapa bentuk, tetapi sepertinya tidak menggunakannya untuk JAX-RS mungkin hanya JAX-WS).

Terlepas jika Anda menggunakan filter ini, filter ini akan menambahkan header ke setiap layanan web REST.

package com.yourdomain.package;

import java.io.IOException;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;

@Provider
public class CORSFilter implements ContainerResponseFilter {

   @Override
   public void filter(final ContainerRequestContext requestContext,
                      final ContainerResponseContext cres) throws IOException {
      cres.getHeaders().add("Access-Control-Allow-Origin", "*");
      cres.getHeaders().add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization");
      cres.getHeaders().add("Access-Control-Allow-Credentials", "true");
      cres.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
      cres.getHeaders().add("Access-Control-Max-Age", "1209600");
   }

}

Kemudian ketika saya menguji dengan curl, responnya memiliki header CORS:

$ curl -D - "http://localhost:8080/rest/test"
HTTP/1.1 200 OK
X-Powered-By: Undertow 1
Access-Control-Allow-Headers: origin, content-type, accept, authorization
Server: Wildfly 8
Date: Tue, 13 May 2014 12:30:00 GMT
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Transfer-Encoding: chunked
Content-Type: application/json
Access-Control-Max-Age: 1209600
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS, HEAD

Pemahaman saya adalah anotasi @Provider yang memberitahu runtime JAX-RS untuk menggunakan filter, tanpa anotasi tidak ada yang terjadi.

Saya mendapat ide tentang penggunaan ContainerResponseFilter dari Contoh jersey.

person Joel Pearson    schedule 13.05.2014
comment
ya saya menggunakan solusi yang sama yang Anda posting di sini. Namun saya yakin kami membuat masalah keamanan dengan mengizinkan setiap koneksi lintas domain. - person Naili; 13.05.2014
comment
Ya, itu benar jika layanan web REST Anda menggunakan cookie untuk mengamankannya. Tetapi jika Anda hanya mencoba membuat beberapa layanan web REST publik maka tidak ada masalah keamanan. Anda tidak menyebutkan dalam pertanyaan Anda jika Anda hanya memerlukan 1 domain untuk berfungsi yaitu services.yourdomain misalnya atau setiap domain. - person Joel Pearson; 14.05.2014
comment
Jika Anda berencana menambahkan host tertentu saja, bukan *, jangan lupa menambahkan portnya juga - person ACV; 08.04.2015
comment
@JoelPearson Bisakah Anda memberi tahu cara mengonfigurasi filter ini? Haruskah saya melakukannya di web.xml? - person Jay; 09.09.2015
comment
@Jay menggunakan anotasi @ Provider, jadi selama CDI Anda diaktifkan untuk telinga/perang Anda, maka tidak diperlukan konfigurasi tambahan - person Joel Pearson; 11.09.2015
comment
+1 untuk anotasi @Provider. Saya juga sedang melihat contoh secara online tetapi entah bagaimana melewatkan bagian penting ini! - person bradimus; 02.10.2015
comment
Saya menjalankan layanan di server jetty dengan wadah jersey servlet. Dalam hal ini, hanya memiliki @Provider saja tidak cukup. Kelas CORSFilter harus didaftarkan sebagai parameter init jersey servlet sebagai berikut: jerseyServlet.setInitParameter( "jersey.config.server.provider.classnames", MyService.class.getCanonicalName() + ", org.glassfish.jersey.media.multipart.MultiPartFeature, " + CORSFilter.class.getCanonicalName()) - person Ashok; 01.12.2015
comment
@Ashok pertanyaan ini tentang Wildfly 8 dan JEE 7, dan bukan jetty/jersey, jadi jika Anda memiliki pertanyaan lain, Anda harus membuat pertanyaan lain, karena solusi ini tidak akan relevan bagi Anda. - person Joel Pearson; 02.12.2015
comment
@Joel Pearson Setuju. Saya menemukan pertanyaan ini melalui pencarian Google untuk mengaktifkan CORS di jax-rs. Saya hanya berpikir ini mungkin bermanfaat bagi orang lain yang membuka halaman ini dengan cara yang sama. Namun demikian, jawaban yang diterima tampaknya tidak menyarankan sesuatu yang spesifik untuk lalat liar atau jee7. Mungkinkah saya kehilangan tautannya? Ini adalah solusi yang berhasil bagi saya di jetty+jersey dan terima kasih telah mempostingnya. - person Ashok; 02.12.2015
comment
@Ashok benar itu mungkin bukan JEE 7, sepertinya anotasi Penyedia tersedia di JEE 6: docs.Oracle.com/javaee/6/api/index.html?javax/ws/rs/ext/ Yang menjadikannya Wildfly adalah faktanya Anda memerlukan CDI agar dapat berfungsi apa adanya. Namun mungkin saja ini akan berfungsi di container JEE 6 mana pun karena tidak mereferensikan Wildfly API secara langsung. Menariknya, Anda masih bisa menjalankannya di JAX-RS di luar JEE. Informasi menarik, terima kasih. - person Joel Pearson; 03.12.2015
comment
Access-Control-Allow-Credentials: true baris ini sebenarnya bertentangan dengan "Access-Control-Allow-Origin", "*" karena ini mungkin merupakan masalah keamanan, atau (setidaknya di Chrome) ditolak. Jika Anda ingin mengizinkan kredensial, Anda perlu menentukan asal tertentu, bukan wildcard. - person Stijn de Witt; 02.02.2016
comment
Pertama dapatkan permintaan asal: String origin = requestContext.getHeaderString("origin");, lalu (ketika origin !== null), gunakan asal itu sebagai wildcard: cres.getHeaders().add("Access-Control-Allow-Origin", origin);. Jika API Anda menggunakan cookie untuk autentikasi (misalnya saat menggunakan autentikasi terkelola kontainer), periksa asal dengan daftar putih asal yang diizinkan sebelum menyetel header CORS. - person Stijn de Witt; 15.02.2016
comment
Terima kasih banyak kawan, bagi yang bertanya-tanya kapan harus memasukkan kode ini, Anda hanya perlu kode ini pada paket org.domainanda.rest Anda, jika Anda menggunakan wildfly 10. - person netto; 17.10.2016
comment
Kami mengalami masalah, yaitu penyedia tidak terdaftar secara default dan pertanyaan berikut membantu: stackoverflow.com/questions/32328012/ - person boutta; 23.11.2017
comment
@Naili, hanya ada masalah keamanan jika aplikasi Anda membatasi akses berdasarkan nomor IP (yang merupakan praktik yang sangat buruk), hanya dalam kasus ini situs jahat dapat mengakses data yang tidak dapat diambil. dalam semua keadaan lainnya, karena kredensial tidak diperbolehkan (seperti dicatat oleh Stijn de Witt) tidak ada masalah keamanan. - person Reto Gmür; 27.02.2018
comment
Ini berfungsi untuk saya ketika menerapkan terhadap JBoss EAP 7.1 hanya jika saya secara eksplisit menambahkan instance CORSFilter di singleton JaxRsApplication. @Provider sendiri tidak berhasil untuk saya. - person Marcus Junius Brutus; 29.01.2020
comment
Bisakah saya menghapus/menonaktifkan header respons default seperti Server: Wildfly 8? - person Ahmad Nadeem; 11.05.2020
comment
Ini tidak cukup. Jika ada otentikasi, Anda juga harus memastikan itu tidak berlaku untuk permintaan OPTIONS, sebaiknya dengan menambahkan @OPTIONS @Path("{path : .*}") handler per nemesisfixx jawaban. - person Jan Hudec; 14.08.2020

Saya menghadapi masalah serupa, dan telah mencoba menggunakan solusi @Alex Petty, tetapi selain harus mengatur header CORS pada setiap titik akhir JAX-RS di kelas saya, seperti:

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getMemberList() {
    List<Member> memberList = memberDao.listMembers();
    members.addAll(memberList);
    return Response
            .status(200)
            .header("Access-Control-Allow-Origin", "*")
            .header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization")
            .header("Access-Control-Allow-Credentials", "true")
            .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD")
            .header("Access-Control-Max-Age", "1209600")
            .entity(memberList)
            .build();
}

Saya harus mendefinisikan lebih lanjut titik akhir OPTIONS penampung semua yang akan mengembalikan header CORS untuk permintaan OPTIONS lainnya di kelas, dan dengan demikian menangkap semua titik akhir semacam itu:

@OPTIONS
@Path("{path : .*}")
public Response options() {
    return Response.ok("")
            .header("Access-Control-Allow-Origin", "*")
            .header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization")
            .header("Access-Control-Allow-Credentials", "true")
            .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD")
            .header("Access-Control-Max-Age", "1209600")
            .build();
}

Hanya setelah melakukan ini, saya dapat menggunakan titik akhir API JAX-RS saya dengan benar dari klien Jquery Ajax di domain atau host lain.

person nemesisfixx    schedule 11.03.2015
comment
Saya mengalami masalah yang aneh. Header respons (.header(SOME_HEADER,SOME_HEADER) yang saya atur tidak dirender dalam UI angkuh di Google chrome, Mozilla tetapi di IE 11. Saya telah memeriksa bahwa header respons ini datang ke browser (di header respons konsol alat pengembang ) tetapi hal yang sama tidak ditampilkan di Swagger UI. - person Debaprasad Jana; 28.12.2015
comment
Itu tidak benar. Anda harus menggunakan filter. Gunakan Filter ketika Anda ingin memfilter dan/atau mengubah permintaan berdasarkan kondisi tertentu. Gunakan Servlet ketika Anda ingin mengontrol, praproses dan/atau permintaan pascaproses. docs.oracle.com/javaee/6/tutorial/doc/bnagb. html - person max_dev; 11.06.2016
comment
Saya baru saja menambahkan .header(Access-Control-Allow-Origin, *) ini dan berhasil - person Pini Cheyni; 06.05.2017

Saya menemukan cara yang lebih mudah (khusus RestEasy) untuk mengaktifkan CORS di Wildfly tanpa menggunakan filter dan di mana Anda dapat mengontrol konfigurasi header respons API di tingkat sumber daya.

Misalnya:

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getMemberList() {
    List<Member> memberList = memberDao.listMembers();
    members.addAll(memberList);
    return Response
            .status(200)
            .header("Access-Control-Allow-Origin", "*")
            .header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization")
            .header("Access-Control-Allow-Credentials", "true")
            .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD")
            .header("Access-Control-Max-Age", "1209600")
            .entity(memberList)
            .build();
}
person Alex Petty    schedule 08.08.2014
comment
Terima kasih. Saya tidak bisa mendapatkan kelas 'javax.ws.rs.container' (mengapa? versi JAX-RS yang lain?), dan solusi Anda berfungsi dengan baik. - person Guildenstern70; 12.09.2014
comment
Cara ini hanya berfungsi untuk GET, ketika mencoba dengan POST (dari klien) terjadi kesalahan yang sama, saya masih mencoba mencari cara untuk mengatasi masalah ini, karena asal saya akan bervariasi setiap saat. - person Francisco Souza; 10.09.2015
comment
Ini tidak akan berhasil, itu salah. CORS pertama-tama panggil metode HTTP OPTIONS sebelum memanggil GET, POST atau apa pun. Entri header harus didefinisikan dalam metode OPTIONS. Jika tidak, browser tidak akan menerimanya. Pemfilteran permintaan HTTP adalah cara terbaik untuk melakukannya. Satu hal lagi, jika Anda menggunakan server HTTP yang serius, seperti Apache, entri Access-Control-Allow-Origin, * terlalu jarang, dan browser tidak mengizinkannya. Anda harus secara khusus menambahkan DNS otorisasi, mis. Access-Control-Allow-Origin: my.domain.com. - person jbrios777; 10.08.2016
comment
Tetapi jika Anda memiliki 100 metode... pendekatan yang lebih baik adalah dengan menggunakan filter - person ACV; 30.08.2016
comment
Jawaban lengkap dengan penjelasan yang tepat untuk permintaan pra-penerbangan telah diberikan oleh @Paul Samsotha dan berikut tautannya - stackoverflow.com/a/ 28067653/3257644 - person Mahesh; 17.02.2020

Saya beruntung mengonfigurasi Berbagi Sumber Daya Lintas Asal (CORS) untuk API saya (di Wildfly) dengan menggunakan lib ini:

<dependency>
<groupId>com.thetransactioncompany</groupId>
<artifactId>cors-filter</artifactId>
<version>2.1</version>
</dependency>

Sangat mudah untuk mengaturnya. Cukup tambahkan ketergantungan di atas ke pom Anda dan kemudian tambahkan konfigurasi berikut ke bagian webapp dari file web.xml Anda.

<filter>
    <filter-name>CORS</filter-name>
    <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>

    <init-param>
        <param-name>cors.allowGenericHttpRequests</param-name>
        <param-value>true</param-value>
    </init-param>

    <init-param>
        <param-name>cors.allowOrigin</param-name>
        <param-value>*</param-value>
    </init-param>

    <init-param>
        <param-name>cors.allowSubdomains</param-name>
        <param-value>false</param-value>
    </init-param>

    <init-param>
        <param-name>cors.supportedMethods</param-name>
        <param-value>GET, HEAD, POST, DELETE, OPTIONS</param-value>
    </init-param>

    <init-param>
        <param-name>cors.supportedHeaders</param-name>
        <param-value>*</param-value>
    </init-param>

    <init-param>
        <param-name>cors.supportsCredentials</param-name>
        <param-value>true</param-value>
    </init-param>

    <init-param>
        <param-name>cors.maxAge</param-name>
        <param-value>3600</param-value>
    </init-param>

</filter>

<filter-mapping>
    <!-- CORS Filter mapping -->
    <filter-name>CORS</filter-name>
    <url-pattern>*</url-pattern>
</filter-mapping>

Anda juga dapat mengonfigurasinya dengan file properti jika Anda mau. Lib ini bekerja dengan sangat baik dan memberi Anda banyak fleksibilitas konfigurasi!

person Alex Petty    schedule 27.06.2014
comment
Saya ingin mencobanya tetapi saya mengalami kesalahan ini di konsol log saya: The 'Access-Control-Allow-Origin' header contains multiple values 'http://127.0.0.1, *', but only one is allowed. Ada ide apa yang harus diubah di web.xml untuk memperbaikinya? - person Ayyoub; 22.07.2015
comment
Saya mencoba ini, tetapi tidak dapat melihat ini ditambahkan ke header. - person Jay; 09.09.2015

Tidak ada jawaban lain yang berhasil untuk saya, tetapi yang ini berhasil:

import javax.ws.rs.core.Response;

Kemudian ubah tipe pengembalian metode layanan menjadi Response, dan ubah pernyataan return menjadi:

return Response.ok(resp).header("Access-Control-Allow-Origin", "*").build();

Dimana resp adalah objek respons asli.

person Alex R    schedule 18.07.2015
comment
Ini hanya berfungsi jika permintaannya sederhana. Itu jika permintaannya adalah GET atau POST dan header permintaannya sederhana (Satu-satunya header sederhana adalah Accept, Accept-Language, Content-Language, Content-Type= application/x-www-form-urlencoded, multipart/form-data, teks/polos). Jika tidak, browser akan mengirimkan permintaan OPTIONS sebelum permintaan sebenarnya. - person Ranuka; 03.12.2016

Anda juga dapat menerapkan javax.ws.rs.core.Feature seperti di bawah ini untuk mengimplementasikan CORS.

import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.ext.Provider;
import org.jboss.resteasy.plugins.interceptors.CorsFilter;

@Provider
 public class CorsFeature implements Feature {

  @Override
  public boolean configure(FeatureContext context) {
    CorsFilter corsFilter = new CorsFilter();
    corsFilter.getAllowedOrigins().add("*");
    context.register(corsFilter);
    return true;
 }

}
person Ranuka    schedule 06.12.2016

Solusi untuk ini menambahkan beberapa header pada responsnya. Di spring atau spring boot memiliki anotasi, tetapi di sistem yang lebih lama, mungkin tidak ada anotasi. Anda dapat mengatasinya dengan menambahkan filter.

Kelas filter:

package com.koushik.restapis.intercepter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;


public class RestCorsFilter implements Filter {

    @Override
    public void destroy() {
    enter code here
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
    
        HttpServletResponse resp = (HttpServletResponse) response;
        resp.addHeader("Access-Control-Allow-Origin", "*");
        resp.addHeader("Access-Control-Allow-Headers", "*");
        resp.addHeader("Access-Control-Allow-Methods", "*");
        
        chain.doFilter(request, resp);
        
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
        
    }

}

web.xml

  <filter>
    <filter-name>RestCorsFilter</filter-name>
    <filter-class>com.koushik.restapis.RestCorsFilter</filter-class>
  </filter>
    <filter-mapping>
    <filter-name>RestCorsFilter</filter-name>
    <url-pattern>/apis/*</url-pattern>
  </filter-mapping>
person 404Koushik    schedule 05.09.2020

Jawaban @Joel Pearson membantu tetapi bagi seseorang yang baru mengenal JAX-RS seperti saya dan menjalankan layanan di Tomcat dengan mengonfigurasi web.xml harus berhati-hati saat membuat kelas dan meletakkannya di mana saja dalam proyek. Lihat paket yang Anda tentukan di bawah jersey dan buat kelas filter ini di sana. Dengan cara itu itu berhasil untuk saya.

person Novice_JS    schedule 14.09.2018

Hanya untuk menambahkan sesuatu pada tanggapan lain. Mengizinkan * sedikit berbahaya. Yang bisa dilakukan adalah mengkonfigurasi database asal yang diperbolehkan (bisa berupa file)

Kemudian ketika permintaan tiba Anda dapat melakukan:

// this will return you the origin 
String[] referers = requestContext.getHeaders().get("referer")

// then search in your DB if the origin is allowed
if (referers != null && referers.lenght == 1 && isAllowedOriging(referers[0])) {
        containerResponseContext.getHeaders().add("Access-Control-Allow-Origin", referers[0]);
        containerResponseContext.getHeaders().add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization, <HERE PUT YOUR DEDICATED HEADERS>);
        containerResponseContext.getHeaders().add("Access-Control-Allow-Credentials", "true");
        containerResponseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
        containerResponseContext.getHeaders().add("Access-Control-Max-Age", "1209600");
}

Dengan cara itu Anda tidak akan mengizinkan semua orang.

person Geoffrey    schedule 05.06.2017