когда нечего делать, а творческая душа требует реализации и хочется что-то сделать или написать, то я пишу сюда или делаю что-то на этом сайте. Кому интересна web-разработка, wordpress и то что рядом с этим, то заходите на мой сайт и читайте всякое...
Йо-йо! Недавно я столкнулся с задачей — создать сайт на котором отображаются бонусы клиента. Бонусы я получаю с web-сервиса из 1С, он отдаёт мне json с данными, но предварительно там я должен авторизоваться.
Для создания этого сайта я решил использовать reactjs. Сайт довольно простой и многого там не будет, но нужно сделать авторизацию. Авторизация происходила с помощью передачи заголовка Authorization примерно вот так:
Authorization: Basic 0ZHJadMU0KI6
Но как только я сделать fetch и запросить данные я столкнулся с множеством проблем в том числе CORS. Я решил эти проблемы и теперь хочу поделиться своим опытом.
Fetch и авторизация
Чтобы fetch мог передавать данные для авторизации нужно явно «сказать» ему, что в нём передаются данные для авторизации с помощью credentials. Пример
// Умышленно опускаю прочий код компонента, сейчас не важно.
componentDidMount(){
const url = 'https://172.168.0.1/api/v1/methdeName?id="123213"';
const headers = new Headers({
'Authorization': '0ZHJadMU0KI6'
});
const options = {
headers,
credentials:"include" // Вот, что нужно задать
};
fetch(url, options).then((response)=>{
console.log(response.json());
})
}
В этом случае всё пройдёт гладко и вы успешно пройдёте авторизацию.
Fetch и CORS
CORS сильно портит жизнь веб-мастеру, но зато защищает нас. Чтобы описать описать взаимодействие fecth и сервера в корсcдоменных запросах я приведу несколько примеров из спецификации fetch.
Пример 1
Скрипт на https: //foo.invalid/ хочет получить некоторые данные с https: //bar.invalid/. (Ни учетные данные, ни доступ к заголовку ответа не важны.)
var url = "https://bar.invalid/api?key=730d67a37d7f3d802e96396d00280768773813fbe726d116944d814422fc1a45&data=about:unicorn";
fetch(url).then(success, failure)
При этом будет использоваться протокол CORS, хотя он полностью прозрачен для разработчика из foo.invalid. Как часть протокола CORS, пользовательский агент будет включать заголовок Origin в запрос:
Origin: https://foo.invalid
Получив ответ от bar.invalid, пользовательский агент проверит заголовок ответа «Access-Control-Allow-Origin». Если его значение равно https: // foo.invalid или *, пользовательский агент вызовет успешный обратный (success) вызов. Если оно имеет какое-либо другое значение или отсутствует, пользовательский агент вызовет failure.
Пример 2
Разработчик foo.invalid вернулся и теперь хочет получить некоторые данные из bar.invalid, одновременно обращаясь к заголовку ответа.
bar.invalid предоставляет правильный заголовок ответа Access-Control-Allow-Origin в соответствии с предыдущим примером. Значения hsts и csp будут зависеть от заголовка ответа «Access-Control-Expose-Headers». Например, если в ответ включены следующие заголовки
тогда hsts будет нулевым, а csp будет «default-src ‘self», даже если ответ включает оба заголовка. Это связано с тем, что bar.invalid должен явно разделять каждый заголовок, перечисляя их имена в заголовке ответа Access-Control-Expose-Headers.
В качестве альтернативы, если bar.invalid хочет совместно использовать все свои заголовки ответа, для запросов, которые не включают учетные данные, он может использовать ‘*’ в качестве значения для заголовка ответа Access-Control-Expose-Headers. Если бы запрос включал учетные данные, имена заголовков ответов должны были бы быть перечислены явно, и ‘*’ не мог бы использоваться.
Пример 3. С передачей учётных данных (пароля)
Разработчик foo.invalid извлекает некоторые данные из bar.invalid, включая учетные данные. На этот раз протокол CORS больше не прозрачен для разработчика, поскольку учетные данные требуют явного согласия:
Это также делает все заголовки ответа Set-Cookie bar.invalid полностью функциональными (в противном случае они игнорируются).
Пользовательский агент обязательно включит в запрос все соответствующие учетные данные. Это также повысит требования к ответу. Мало того, что bar.invalid нужно будет перечислить https: // foo.invalid в качестве значения для заголовка Access-Control-Allow-Origin (‘*’ не допускается, когда задействованы учетные данные), Access-Control-Allow-Credentials заголовок также должен присутствовать:
Если ответ не включает эти два заголовка с этими значениями, будет вызван failure callback (в fetch). Однако любые заголовки ответа Set-Cookie будут соблюдены.
CORS, Fetch и сервер
Как мы уже поняли нам нужно задавать правильные заголовки для того, чтобы кросс-доменные запросы работали и мы имели доступ к контенту, который присылает сервер. Вот выжимка заголовков:
// Нет авторизации
Access-Control-Allow-Origin: *
// Нужна авторизация, помним, что обызателен https на сервере и клиенте
Access-Control-Allow-Origin: https://example.ru
Access-Control-Allow-Credentials: true
// Нужен доступ к заголовкам ответа
Access-Control-Expose-Headers: *
Во время кросс-доменных запросов get-запрос преобразуется в option в том случае если вы передаёте заголовки и в fetch’е установлено {credentials:»include»}. В таком случае появляется ошибка » 405 method not allowed».
Options-запрос отправляется для того, чтобы браузер понял можно ли вообще передавать заголовки с этого домена и какие.
В том случае если вы передаёте в get-запросе, например заголовок «Authorization» то должны отдать «пачку» заголовков:
Access-Control-Allow-Credentials: true
// Тут перечисляем наши заголовки
Access-Control-Allow-Headers: Authorization, прочие заголовки запроса...
// Перечисляем разрешённые методы
Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS
// Пишем домен с которого отправляем запрос
Access-Control-Allow-Origin: https://yourdomain.ru
Когда fetch получит эти заголовки то автоматически отправит уже GET-запрос. Вы можете легко проверить это если посмотрите, например, в DevTools хрома (вкладка Network)
В том случае если заголовки не будут переданы то в status code от сервера вы увидите «405 method not allowed». Так же вы увидите это если не перечислите нужный метод в «Access-Control-Allow-Methods»
Если вы не понимаете, что такое fetch предлагаю прочитать мою статью «fetch в reactjs«, возможно так же вам понадобиться статья про router в react
Надеюсь я сэкономил вам немного времени, ведь именно это основная цель моего сайта. Если вы хотите и дальше экономить своё время то обязательно подписывайтесь на обновления сайта с помощью push-уведомлений.
Поддержи Xakplant
Я давно хочу развить видеоверсию, но пока этого не получается из-за нехватки ресурсов. Сейчас я собираю деньги на новый компьютер и микрофон. Поддержи xaklant и ты увидишь полезные видео быстрее.