Không có tiêu đề 'Kiểm soát truy cập-Cho phép-Xuất xứ' trên tài nguyên được yêu cầu, khi cố lấy dữ liệu từ API REST


469

Tôi đang cố gắng tìm nạp một số dữ liệu từ API REST của HP Alm. Nó hoạt động khá tốt với một tập lệnh curl nhỏ - tôi lấy dữ liệu của mình.

Bây giờ làm điều đó với JavaScript, tìm nạp và ES6 (ít nhiều) dường như là một vấn đề lớn hơn. Tôi tiếp tục nhận được thông báo lỗi này:

Tìm nạp API không thể tải. Trả lời yêu cầu preflight không vượt qua kiểm tra kiểm soát truy cập: Không có tiêu đề 'Kiểm soát truy cập-Cho phép-Xuất xứ' trên tài nguyên được yêu cầu. Do đó, nguồn gốc ' http://127.0.0.1:3000 ' không được phép truy cập. Phản hồi có mã trạng thái HTTP 501. Nếu phản hồi mờ phục vụ nhu cầu của bạn, hãy đặt chế độ của yêu cầu thành 'no-cors' để tìm nạp tài nguyên khi CORS bị vô hiệu hóa.

Tôi hiểu rằng điều này là do tôi đang cố gắng tìm nạp dữ liệu đó từ trong localhost của mình và giải pháp nên sử dụng CORS. Bây giờ tôi nghĩ rằng tôi thực sự đã làm điều đó, nhưng bằng cách nào đó nó bỏ qua những gì tôi viết trong tiêu đề hoặc vấn đề là cái gì khác?

Vì vậy, có một vấn đề thực hiện? Tôi đang làm sai à? Tôi không thể kiểm tra nhật ký máy chủ một cách đáng tiếc. Tôi thực sự có một chút mắc kẹt ở đây.

function performSignIn() {

  let headers = new Headers();

  headers.append('Content-Type', 'application/json');
  headers.append('Accept', 'application/json');

  headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
  headers.append('Access-Control-Allow-Credentials', 'true');

  headers.append('GET', 'POST', 'OPTIONS');

  headers.append('Authorization', 'Basic ' + base64.encode(username + ":" + password));

  fetch(sign_in, {
      //mode: 'no-cors',
      credentials: 'include',
      method: 'POST',
      headers: headers
    })
    .then(response => response.json())
    .then(json => console.log(json))
    .catch(error => console.log('Authorization failed : ' + error.message));
}

Tôi đang sử dụng Chrome. Tôi cũng đã thử sử dụng Plugin Chrome CORS đó, nhưng sau đó tôi nhận được một thông báo lỗi khác:

Giá trị của tiêu đề 'Kiểm soát truy cập-Cho phép-Xuất xứ' trong phản hồi không được là ký tự đại diện '*' khi chế độ thông tin đăng nhập của yêu cầu là 'bao gồm'. Do đó, nguồn gốc ' http://127.0.0.1:3000 ' không được phép truy cập. Chế độ thông tin xác thực của các yêu cầu do XMLHttpRequest khởi xướng được kiểm soát bởi thuộc tính withCredentials.

Câu trả lời:


824

Câu trả lời này bao gồm rất nhiều nền tảng, vì vậy nó được chia thành ba phần:

  • Cách sử dụng proxy CORS để khắc phục các vấn đề về tiêu đề 'Không có quyền truy cập-Kiểm soát-Cho phép-Xuất xứ'
  • Làm thế nào để tránh ánh sáng trước CORS
  • Cách khắc phục tiêu đề của Access Access-Control-Allow-Origin không phải là vấn đề về ký tự đại diện

Cách sử dụng proxy CORS để khắc phục các vấn đề về tiêu đề 'Không có quyền truy cập-Kiểm soát-Cho phép-Xuất xứ'

Nếu bạn không kiểm soát máy chủ, mã JavaScript frontend của bạn đang gửi yêu cầu và vấn đề với phản hồi từ máy chủ đó chỉ là thiếu Access-Control-Allow-Origintiêu đề cần thiết , bạn vẫn có thể khiến mọi thứ hoạt động bằng cách thực hiện yêu cầu thông qua một Proxy CORS. Để hiển thị cách thức hoạt động, trước tiên, đây là một số mã không sử dụng proxy CORS:

const url = "https://example.com"; // site that doesn’t send Access-Control-*
fetch(url)
.then(response => response.text())
.then(contents => console.log(contents))
.catch(() => console.log("Can’t access " + url + " response. Blocked by browser?"))

Lý do catchkhối bị tấn công là, trình duyệt ngăn mã đó truy cập vào phản hồi từ đó https://example.com. Và lý do trình duyệt làm điều đó là, phản hồi thiếu Access-Control-Allow-Origintiêu đề phản hồi.

Bây giờ, đây chính xác là ví dụ tương tự nhưng chỉ với một proxy CORS được thêm vào:

const proxyurl = "https://cors-anywhere.herokuapp.com/";
const url = "https://example.com"; // site that doesn’t send Access-Control-*
fetch(proxyurl + url) // https://cors-anywhere.herokuapp.com/https://example.com
.then(response => response.text())
.then(contents => console.log(contents))
.catch(() => console.log("Can’t access " + url + " response. Blocked by browser?"))

Lưu ý: Nếu https://cors-anywhere.herokuapp.com không hoạt động hoặc không khả dụng khi bạn dùng thử, hãy xem bên dưới để biết cách triển khai máy chủ CORS Anywhere của bạn tại Heroku chỉ trong 2-3 phút.

Đoạn mã thứ hai ở trên có thể truy cập phản hồi thành công vì lấy URL yêu cầu và thay đổi nó thành https://cors-anywhere.herokuapp.com/https://example.com .byby chỉ cần thêm tiền tố vào URL proxy. yêu cầu được thực hiện thông qua proxy đó, sau đó:

  1. Chuyển tiếp yêu cầu đến https://example.com.
  2. Nhận được phản hồi từ https://example.com.
  3. Thêm Access-Control-Allow-Origintiêu đề để trả lời.
  4. Chuyển phản hồi đó, với tiêu đề được thêm đó, trở lại mã giao diện yêu cầu.

Trình duyệt sau đó cho phép mã frontend truy cập vào phản hồi, bởi vì phản hồi đó với Access-Control-Allow-Origintiêu đề phản hồi là những gì trình duyệt nhìn thấy.

Bạn có thể dễ dàng chạy proxy của riêng mình bằng mã từ https://github.com/Rob--W/cors-anywhere/ .
Bạn cũng có thể dễ dàng triển khai proxy của riêng mình lên Heroku chỉ trong 2-3 phút, với 5 lệnh:

git clone https://github.com/Rob--W/cors-anywhere.git
cd cors-anywhere/
npm install
heroku create
git push heroku master

Sau khi chạy các lệnh đó, bạn sẽ kết thúc với máy chủ CORS Anywhere của mình đang chạy tại, ví dụ: https://cryptic-headland-94862.herokuapp.com/ . Vì vậy, thay vì tiền tố URL yêu cầu của bạn với https://cors-anywhere.herokuapp.com, tiền tố thay vào đó bằng URL cho ví dụ của riêng bạn; ví dụ: https://cryptic-headland-94862.herokuapp.com/https://example.com .

Vì vậy, nếu khi bạn cố gắng sử dụng https://cors-anywhere.herokuapp.com, bạn sẽ thấy nó bị hỏng (đôi khi sẽ như vậy), sau đó xem xét lấy tài khoản Heroku (nếu bạn chưa có) và lấy 2 hoặc 3 phút để thực hiện các bước trên để triển khai máy chủ CORS Anywhere của riêng bạn trên Heroku.

Bất kể, cho dù bạn tự chạy hay sử dụng https://cors-anywhere.herokuapp.com hoặc proxy mở khác, giải pháp này vẫn hoạt động ngay cả khi yêu cầu là một trình duyệt kích hoạt yêu cầu trình duyệt trước CORS OPTIONSvì trong trường hợp đó, proxy cũng gửi lại Access-Control-Allow-HeadersAccess-Control-Allow-Methodscác tiêu đề cần thiết để làm cho preflight thành công.


Làm thế nào để tránh ánh sáng trước CORS

Mã trong câu hỏi kích hoạt tiền tố CORS trước khi nó gửi một Authorizationtiêu đề.

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_Vquests

Ngay cả khi không có điều đó, Content-Type: application/jsontiêu đề cũng sẽ kích hoạt preflight.

Điều gì có nghĩa là tiền tố của Nhật Bản: trước khi trình duyệt thử POSTmã trong câu hỏi, trước tiên, nó sẽ gửi OPTIONSyêu cầu đến máy chủ - để xác định xem máy chủ có chọn tham gia nhận nguồn gốc chéo POSTbao gồm tiêu đề AuthorizationContent-Type: application/jsontiêu đề hay không.

Nó hoạt động khá tốt với một tập lệnh curl nhỏ - tôi lấy dữ liệu của mình.

Để kiểm tra đúng cách curl, bạn phải mô phỏng OPTIONSyêu cầu preflight mà trình duyệt gửi:

curl -i -X OPTIONS -H "Origin: http://127.0.0.1:3000" \
    -H 'Access-Control-Request-Method: POST' \
    -H 'Access-Control-Request-Headers: Content-Type, Authorization' \
    "https://the.sign_in.url"

... với https://the.sign_in.urlthay thế bởi bất cứ điều gì thực tế của bạn sign_inURL.

Phản hồi mà trình duyệt cần xem từ OPTIONSyêu cầu đó phải bao gồm các tiêu đề như thế này:

Access-Control-Allow-Origin:  http://127.0.0.1:3000
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type, Authorization

Nếu OPTIONSphản hồi không bao gồm các tiêu đề đó, thì trình duyệt sẽ dừng ngay tại đó và thậm chí không bao giờ cố gắng gửi POSTyêu cầu. Ngoài ra, mã trạng thái HTTP cho phản hồi phải là 2xx, thường là 200 hoặc 204. Nếu đó là bất kỳ mã trạng thái nào khác, trình duyệt sẽ dừng ngay tại đó.

Máy chủ trong câu hỏi đang trả lời OPTIONSyêu cầu bằng mã trạng thái 501, điều này rõ ràng có nghĩa là nó đang cố gắng chỉ ra rằng nó không thực hiện hỗ trợ cho OPTIONScác yêu cầu. Các máy chủ khác thường phản hồi với Phương thức 405 không được phép sử dụng mã trạng thái trong trường hợp này.

Vì vậy, bạn sẽ không bao giờ có thể thực hiện POSTcác yêu cầu trực tiếp đến máy chủ đó từ mã JavaScript frontend của mình nếu máy chủ đáp ứng OPTIONSyêu cầu đó bằng 405 hoặc 501 hoặc bất cứ điều gì khác ngoài 200 hoặc 204 hoặc nếu không đáp ứng với những điều cần thiết đó tiêu đề phản hồi.

Cách để tránh kích hoạt một ánh sáng trước cho trường hợp trong câu hỏi sẽ là:

  • nếu máy chủ không yêu Authorizationcầu tiêu đề yêu cầu mà thay vào đó (ví dụ) dựa vào dữ liệu xác thực được nhúng trong phần thân của POSTyêu cầu hoặc dưới dạng tham số truy vấn
  • nếu máy chủ không yêu cầu phần POSTthân phải có Content-Type: application/jsonloại phương tiện mà thay vào đó chấp nhận phần POSTthân như application/x-www-form-urlencodedvới một tham số có tên json(hoặc bất cứ thứ gì) có giá trị là dữ liệu JSON

Cách khắc phục tiêu đề của Access Access-Control-Allow-Origin không phải là vấn đề về ký tự đại diện

Tôi nhận được một thông báo lỗi khác:

Giá trị của tiêu đề 'Kiểm soát truy cập-Cho phép-Xuất xứ' trong phản hồi không được là ký tự đại diện '*' khi chế độ thông tin đăng nhập của yêu cầu là 'bao gồm'. Do đó, nguồn gốc ' http://127.0.0.1:3000 ' không được phép truy cập. Chế độ thông tin xác thực của các yêu cầu do XMLHttpRequest khởi xướng được kiểm soát bởi thuộc tính withCredentials.

Đối với yêu cầu bao gồm thông tin đăng nhập, các trình duyệt sẽ không cho phép mã JavaScript frontend của bạn truy cập vào phản hồi nếu giá trị của Access-Control-Allow-Origintiêu đề phản hồi là *. Thay vào đó, giá trị trong trường hợp đó phải khớp chính xác với nguồn gốc của mã frontend của bạn , http://127.0.0.1:3000.

Xem các yêu cầu được xác thực và ký tự đại diện trong bài viết Kiểm soát truy cập HTTP (CORS) MDN.

Nếu bạn kiểm soát máy chủ mà bạn đang gửi yêu cầu, thì một cách phổ biến để giải quyết trường hợp này là cấu hình máy chủ để lấy giá trị của Origintiêu đề yêu cầu và phản hồi / phản ánh lại giá trị của Access-Control-Allow-Origintiêu đề phản hồi. Ví dụ: với nginx:

add_header Access-Control-Allow-Origin $http_origin

Nhưng đó chỉ là một ví dụ; hệ thống máy chủ (web) khác cung cấp các cách tương tự với giá trị gốc echo.


Tôi đang sử dụng Chrome. Tôi cũng đã thử sử dụng Plugin Chrome CORS đó

Plugin Chrome CORS rõ ràng chỉ đơn giản là tiêm một Access-Control-Allow-Origin: *tiêu đề vào phản hồi mà trình duyệt nhìn thấy. Nếu plugin thông minh hơn, những gì nó sẽ làm là đặt giá trị của Access-Control-Allow-Origintiêu đề phản hồi giả mạo đó thành nguồn gốc thực tế của mã JavaScript frontend của bạn , http://127.0.0.1:3000.

Vì vậy, tránh sử dụng plugin đó, ngay cả để thử nghiệm. Nó chỉ là một sự xao lãng. Nếu bạn muốn kiểm tra những phản hồi bạn nhận được từ máy chủ mà không có trình duyệt lọc chúng, bạn nên sử dụng curl -Hnhư trên.


Theo như mã JavaScript frontend cho fetch(…)yêu cầu trong câu hỏi:

headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
headers.append('Access-Control-Allow-Credentials', 'true');

Loại bỏ những dòng. Các Access-Control-Allow-*tiêu đề là tiêu đề phản ứng . Bạn không bao giờ muốn gửi chúng trong một yêu cầu. Hiệu ứng duy nhất sẽ có là kích hoạt trình duyệt để thực hiện một ánh sáng trước.


1
Vì vậy, url sẽ trông giống như: 127.0.0.1:9999/127.0.0.1 giáp000 với cors, phải không?
daniel.lozynski

Nếu bạn đang chạy phiên bản riêng của mã github.com/Rob--W/cors-anywhere127.0.0.1:9999 và bạn muốn gửi yêu cầu tới 127.0.0.1 giáp000 , vâng
sIDIAbarker

4
Câu trả lời tuyệt vời, vấn đề của tôi là máy chủ từ xa không đáp ứng các yêu cầu TÙY CHỌN, vì vậy sau khi loay hoay với các yêu cầu và tiêu đề cho những gì có vẻ như lâu đời tôi đã giải quyết nó bằng cách xóa các tiêu đề Content-TypeAccess-Control-Allow-Origin- cảm ơn bạn!
Morvael

1
Tôi không thích ý tưởng sử dụng proxy trừ khi bạn kiểm soát, nếu không, bạn sẽ lấy dữ liệu qua một bên thứ ba không đáng tin cậy và nó sẵn sàng để giả mạo, v.v.
DaveMongoose

1
Đó là câu trả lời sâu sắc và tuyệt vời cảm ơn bạn, Điều tôi đã làm khi kiểm tra là Open chrome với cờ bảo mật vô hiệu hóa và nó đã hoạt động. open /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir
Karun

101

Lỗi này xảy ra khi URL máy khách và URL máy chủ không khớp, bao gồm cả số cổng. Trong trường hợp này, bạn cần kích hoạt dịch vụ của mình cho CORS, đó là chia sẻ tài nguyên nguồn gốc chéo.

Nếu bạn đang lưu trữ một dịch vụ Spring REST thì bạn có thể tìm thấy nó trong bài đăng trên blog hỗ trợ CORS trong Spring Framework .

Nếu bạn đang lưu trữ một dịch vụ bằng máy chủ Node.js thì

  1. Dừng máy chủ Node.js.
  2. npm install cors --save
  3. Thêm các dòng sau vào server.js của bạn

    var cors = require('cors')
    
    app.use(cors()) // Use this after the variable declaration

4
Làm việc mà không có bất kỳ vấn đề nào ... rất hữu ích với liên kết tài nguyên mùa xuân .. Cảm ơn rất nhiều
Rajesh Mbm

4
including the port number:(
scottysseus 16/03/18

1
Thực sự hữu ích bạn đã làm cho ngày của tôi
kunal pal

số cổng đã cứu mạng tôi
ashad

Câu trả lời đơn giản nhất từng cảm ơn :)
ngăn xếp

47

Vấn đề phát sinh do bạn đã thêm mã sau đây làm tiêu đề yêu cầu trong giao diện người dùng của mình:

headers.append('Access-Control-Allow-Origin', 'http://localhost:3000');
headers.append('Access-Control-Allow-Credentials', 'true');

Những tiêu đề thuộc về phản ứng , không yêu cầu. Vì vậy, loại bỏ chúng, bao gồm cả dòng:

headers.append('GET', 'POST', 'OPTIONS');

Yêu cầu của bạn có Type Loại nội dung: ứng dụng / json ', do đó đã kích hoạt cái được gọi là đèn pha CORS. Điều này gây ra trình duyệt đã gửi yêu cầu bằng phương thức TÙY CHỌN. Xem preflight CORS để biết thông tin chi tiết.
Do đó, trong back-end của bạn, bạn phải xử lý yêu cầu được chiếu trước này bằng cách trả về các tiêu đề phản hồi bao gồm:

Access-Control-Allow-Origin : http://localhost:3000
Access-Control-Allow-Credentials : true
Access-Control-Allow-Methods : GET, POST, OPTIONS
Access-Control-Allow-Headers : Origin, Content-Type, Accept

Tất nhiên, cú pháp thực tế phụ thuộc vào ngôn ngữ lập trình bạn sử dụng cho back-end của bạn.

Trong front-end của bạn, nó sẽ giống như vậy:

function performSignIn() {
    let headers = new Headers();

    headers.append('Content-Type', 'application/json');
    headers.append('Accept', 'application/json');
    headers.append('Authorization', 'Basic ' + base64.encode(username + ":" +  password));
    headers.append('Origin','http://localhost:3000');

    fetch(sign_in, {
        mode: 'cors',
        credentials: 'include',
        method: 'POST',
        headers: headers
    })
    .then(response => response.json())
    .then(json => console.log(json))
    .catch(error => console.log('Authorization failed : ' + error.message));
}

4
Đây phải là câu trả lời hàng đầu - tôi thực sự không thích ý tưởng bỏ qua CORS, đặc biệt là bằng cách định tuyến nó thông qua bên thứ ba.
DaveMongoose

này, cái gì 'Header ()' làm ơn?
mitsu

@mitsu Nếu bạn tham khảo dòng: let headers = new Headers (); ở trên, sau đó là giao diện tìm nạp API để thực hiện các hành động với yêu cầu http hoặc tiêu đề phản hồi. Truy cập developer.mozilla.org/en-US/docs/Web/API/Headers để biết chi tiết cũng như các ví dụ về việc sử dụng nó.
Lex Soft

1
Câu trả lời rõ ràng và chính xác .. logic và chính xác của nó giải quyết vấn đề. - Thx
Dhammika

7

Trong trường hợp của tôi, tôi sử dụng giải pháp dưới đây

Mặt trước hoặc góc

post(
    this.serverUrl, dataObjToPost,
    {
      headers: new HttpHeaders({
           'Content-Type':  'application/json',
         })
    }
)

back-end (tôi sử dụng php)

header("Access-Control-Allow-Origin: http://localhost:4200");
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header("Access-Control-Allow-Headers: Content-Type, Authorization");

$postdata = file_get_contents("php://input");
$request = json_decode($postdata);
print_r($request);

2

Chỉ hai xu của tôi ... liên quan đến Cách sử dụng proxy CORS để Access-Control-Allow-Originkhắc phục các vấn đề về tiêu đề

Đối với những người bạn làm việc với php ở phần phụ trợ, việc triển khai "proxy CORS" cũng đơn giản như:

  1. tạo một tệp có tên 'no-cors.php' với nội dung sau:

    $URL = $_GET['url']; echo json_encode(file_get_contents($URL)); die();

  2. ở mặt trước của bạn, làm một cái gì đó như:

    fetch('https://example.com/no-cors.php' + '?url=' + url) .then(response=>{*/Handle Response/*})



1

Sử dụng dataType: 'jsonp'làm việc cho tôi.

   async function get_ajax_data(){
       var _reprojected_lat_lng = await $.ajax({
                                type: 'GET',
                                dataType: 'jsonp',
                                data: {},
                                url: _reprojection_url,
                                error: function (jqXHR, textStatus, errorThrown) {
                                    console.log(jqXHR)
                                },
                                success: function (data) {
                                    console.log(data);

                                    // note: data is already json type, you
                                    //       just specify dataType: jsonp
                                    return data;
                                }
                            });


 } // function               

1

Trong trường hợp của tôi, máy chủ web đã ngăn phương thức "TÙY CHỌN"

Kiểm tra máy chủ web của bạn để biết phương pháp tùy chọn

Tôi đang sử dụng "webtier"

/www/webtier/domains/[domainname[/config/fmwconfig/components/OHS/VCWeb1/httpd.conf

<IfModule mod_rewrite.c>
  RewriteEngine on
  RewriteCond %{REQUEST_METHOD} ^OPTIONS
  RewriteRule .* . [F]
</IfModule>

thay đổi thành

<IfModule mod_rewrite.c>
  RewriteEngine off
  RewriteCond %{REQUEST_METHOD} ^OPTIONS
  RewriteRule .* . [F]
</IfModule>

0

Tôi đã làm việc với Spring REST và tôi đã giải quyết nó bằng cách thêm cácMaxMethod vào WebMvcConfigker.

@Value( "${app.allow.origins}" )
    private String allowOrigins;    
@Bean
public WebMvcConfigurer corsConfigurer() {
            System.out.println("allow origin: "+allowOrigins);
            return new WebMvcConfigurerAdapter() {
                @Override
                public void addCorsMappings(CorsRegistry registry) {
                    registry.addMapping("/**")
                    //.allowedOrigins("http://localhost")
                    .allowedOrigins(allowOrigins)
                    .allowedMethods("PUT", "DELETE","GET", "POST");
                }
            };
        }

-1

Tôi đã nhận được lỗi này trên địa phương của tôi. Tôi đã chạy Visual Studio với tư cách quản trị viên và nó đã được giải quyết.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.