CORS: невозможно использовать подстановочный знак в Access-Control-Allow-Origin, когда флаг учетных данных установлен в true

У меня есть настройка, включающая

Фронтенд-сервер (Node.js, домен: localhost:3000) <---> Бэкэнд (Django, Ajax, домен: localhost:8000)

Браузер <- webapp <- Node.js (обслуживать приложение)

Браузер (webapp) -> Ajax -> Django(обслуживать POST-запросы ajax)

Теперь моя проблема заключается в настройке CORS, которую веб-приложение использует для выполнения вызовов Ajax на внутренний сервер. В хроме я продолжаю получать

Невозможно использовать подстановочный знак в Access-Control-Allow-Origin, если флаг учетных данных имеет значение true.

не работает и в Firefox.

Моя настройка Node.js:

var allowCrossDomain = function(req, res, next) {
    res.header('Access-Control-Allow-Origin', 'http://localhost:8000/');
    res.header('Access-Control-Allow-Credentials', true);
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    next();
};

И в Django я использую это промежуточное программное обеспечение вместе с этим

Веб-приложение делает запросы как таковые:

$.ajax({
    type: "POST",
    url: 'http://localhost:8000/blah',
    data: {},
    xhrFields: {
        withCredentials: true
    },
    crossDomain: true,
    dataType: 'json',
    success: successHandler
});

Итак, заголовки запросов, которые отправляет веб-приложение, выглядят так:

Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: "Origin, X-Requested-With, Content-Type, Accept"
Access-Control-Allow-Methods: 'GET,PUT,POST,DELETE'
Content-Type: application/json 
Accept: */*
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Cookie: csrftoken=***; sessionid="***"

И вот заголовок ответа:

Access-Control-Allow-Headers: Content-Type,*
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE
Content-Type: application/json

Куда я иду не так?!

Редактировать 1: я использую chrome --disable-web-security, но теперь хочу, чтобы все действительно работало.

Изменить 2: Ответ:

Итак, решение для меня django-cors-headers конфигурации:

CORS_ORIGIN_ALLOW_ALL = False
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = (
    'http://localhost:3000' # Here was the problem indeed and it has to be http://localhost:3000, not http://localhost:3000/
)

13 ответов

Решение

Это часть безопасности, вы не можете этого сделать. Если вы хотите разрешить учетные данные, то ваш Access-Control-Allow-Origin не должен использовать *, Вам нужно будет указать точный протокол + домен + порт. Для справки смотрите эти вопросы:

  1. Подстановочные поддомены Access-Control-Allow-Origin, порты и протоколы
  2. Совместное использование ресурсов с использованием учетных данных

Кроме того * является слишком разрешительным и будет препятствовать использованию учетных данных. Так установить http://localhost:3000 или же http://localhost:8000 как разрешить заголовок источника.

Если вы используете промежуточное ПО CORS и хотите отправить withCredential логическое значение true, вы можете настроить CORS следующим образом:

var cors = require('cors');    
app.use(cors({credentials: true, origin: 'http://localhost:3000'}));

Расширяя идею @Renaud, cors теперь предоставляет очень простой способ сделать это:

Из официальной документации cors, найденной здесь:

"origin: настраивает заголовок CORS Access-Control-Allow-Origin. Возможные значения: Boolean - установите для origin значение true, чтобы отразить источник запроса, как определено в req.header('Origin'), или установите значение false, чтобы отключить CORS."

Следовательно, мы просто делаем следующее:

const app = express();
const corsConfig = {
    credentials: true,
    origin: true,
};
app.use(cors(corsConfig));

Наконец, я думаю, что стоит упомянуть, что есть случаи, когда мы хотели бы разрешить запросы на перекрестное происхождение от кого угодно; например, при создании общедоступного REST API.

ПРИМЕЧАНИЕ: Я бы хотел оставить это как комментарий к его ответу, но, к сожалению, у меня нет очков репутации.

Попытайся:

const cors = require('cors')

const corsOptions = {
    origin: 'http://localhost:4200',
    credentials: true,

}
app.use(cors(corsOptions));

Если вы используете express вы можете использовать пакет cors, чтобы использовать CORS, вместо написания промежуточного программного обеспечения;

var express = require('express')
, cors = require('cors')
, app = express();

app.use(cors());

app.get(function(req,res){ 
  res.send('hello');
});

Если вы хотите разрешить все источники и сохранить учетные данные, это сработало для меня:

app.use(cors({
  origin: function(origin, callback){
    return callback(null, true);
  },
  optionsSuccessStatus: 200,
  credentials: true
}));

Это работает для меня в разработке, но я не могу посоветовать, что в производстве, это просто другой способ выполнения работы, о котором еще не упоминалось, но, вероятно, не лучший. В любом случае здесь идет:

Вы можете получить источник из запроса, а затем использовать его в заголовке ответа. Вот как это выглядит в экспрессе:

app.use(function(req, res, next) {
  res.header('Access-Control-Allow-Origin', req.header('origin') );
  next();
});

Я не знаю, как это будет выглядеть с вашей настройкой Python, но это должно быть легко перевести.

Для целей разработки в Chrome установка этого дополнения избавит от этой конкретной ошибки:

Access to XMLHttpRequest at 'http://192.168.1.42:8080/sockjs-node/info?t=1546163388687' 
from origin 'http://localhost:8080' has been blocked by CORS policy: The value of the 
'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' 
when the request's credentials mode is 'include'. The credentials mode of requests 
initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

После установки убедитесь, что вы добавили свой шаблон URL в Intercepted URLs нажав на значок AddOn (CORS, зеленый или красный) и заполнив соответствующее текстовое поле. Пример шаблона URL для добавления сюда, который будет работать с http://localhost:8080 было бы: *://*

Хотя у нас есть много решений относительно происхождения cors, я думаю, что могу добавить недостающую часть. Как правило, использование промежуточного программного обеспечения cors в node.js служит максимальной цели, такой как различные методы http (получение, отправка, размещение, удаление).

Но есть варианты использования, такие как отправка ответа cookie, нам нужно включить учетные данные как истинные внутри промежуточного программного обеспечения cors или мы не можем установить cookie . Также есть варианты использования, чтобы дать доступ ко всему происхождению . в этом случае мы должны использовать,

      {credentials: true, origin: true}

Для конкретного источника нам нужно указать имя источника,

      {credential: true, origin: "http://localhost:3000"}

Для нескольких источников,

      {credential: true, origin: ["http://localhost:3000", "http://localhost:3001" ]}

В некоторых случаях нам может потребоваться разрешить несколько источников. Один вариант использования разрешает только разработчикам. Чтобы иметь этот динамический белый список, мы можем использовать такую ​​​​функцию

      const whitelist = ['http://developer1.com', 'http://developer2.com']
const corsOptions = {
origin: (origin, callback) => {
    if (whitelist.indexOf(origin) !== -1) {
      callback(null, true)
    } else {
      callback(new Error())
    }
  }
}

установите 'supports_credentials' => true, в config/cors.php это работает как шарм

ОШИБКА CORS с NETLIFY и HEROKU

На самом деле, если ни одно из вышеперечисленных решений не сработало для вас, вы можете попробовать это. В моем случае серверная часть работала на Heroku, а внешняя — на netlify. в файле .env внешнего интерфейса server_url был записан как

      REACT_APP_server_url = "https://ci-cd-backend.herokuapp.com"

а в бэкэнде все мои вызовы API написаны так:

      app.get('/login', (req, res, err) => {});

Итак, единственное изменение, которое вам нужно сделать, это добавить /api в конце маршрутов,

таким образом, базовый URL-адрес внешнего интерфейса будет выглядеть так:

      REACT_APP_server_url = "https://ci-cd-backend.herokuapp.com/api"

и бэкэнд API должны быть записаны как,

      app.get('/api/login', (req, res, err) => {})

В моем случае это сработало, и я считаю, что эта проблема особенно связана с тем, что внешний интерфейс размещается на netlify.

Была эта проблема с angular, используя перехватчик auth для редактирования заголовка, прежде чем запрос будет выполнен. Мы использовали api-токен для аутентификации, поэтому у меня были включены учетные данные. теперь кажется, что это больше не нужно / разрешено

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    req = req.clone({
      //withCredentials: true, //not needed anymore
      setHeaders: {
        'Content-Type' : 'application/json',
        'API-TOKEN' : 'xxx'
      },
    });
    
    return next.handle(req);
  }

Кроме того, сейчас нет побочных эффектов.

Быстрый и простой обходной путь - отключить CORS, включив параметр ниже в свойствах ярлыка браузера:

--disable-web-security --user-data-dir="[c:]"

Пример:

Удачи!

Другие вопросы по тегам