CORS + Basic Authentication не работает в большинстве браузеров

Пытаюсь получить JSON с внешнего домена. Всё бы ничего, но он защищен с помощью Basic Authentication, что почему-то осложняет доступ многим браузерам.
Свободно вертеть внешним доменом нет возможности, настроить то можно, но он администрируется на стороне клиента и дергать его постоянно нет возможности. Поэтому пришлось создать тестовый стенд в вакууме.

Есть следующий код на внешнем домене:
<?php
	header("Access-Control-Allow-Credentials: true");
	header("Access-Control-Allow-Origin: http://clientdomain");
?>
{ "result": "ok" }


Следующий код на клиенте:
$('button').click(function(){
	$.ajax({
		url: 'http://overestimated.info/script.php',
		type: 'GET',
		crossDomain: true,
		dataType: 'json',
		username: 'user',
		password: 'user',
		success: function(x, status){
			console.log(JSON.stringify(x));
		},
		error: function(x, status, error){
			console.log(status);
			console.log(error);
		}
	});
});


В Chrome и Safari код работает как положено и получает нужный JSON. В остальных же браузерах начинается печаль: если верить Fiddler, то запрос на внешний домен не уходит вовсе, а в консоли приходит ошибка.
Opera: status: «error», error: ""
IE10: status: «error», error: «InvalidAccessError»
FF: status: «error», error: "[Exception… «Access to restricted URI denied» code: «1012» nsresult: «0x805303f4 (NS_ERROR_DOM_BAD_URI)» location: «clientdomain/js/lib/jquery-1.10.0.min.js Line: 6»] { constructor=DOMException, code=1012, INDEX_SIZE_ERR=1, more...}" (там целый объект)

Много гуглил, много пробовал, но пробиться не могу. Есть советы включать withCredentials в запросе
xhrFields: {
	withCredentials: true
}

или

beforeSend: function(xhr){
	xhr.withCredentials = true;
},


Это не помогает, запрос все так же обламывается еще в браузере перед отправкой.
Вот пример. jsfiddle почему-то не нравится IE и Opera, но в Chrome, Safari и Firefox все отрабатывается так как я описал.

Помогите пожалуйста понять, в чем дело.

PS: на тестовом стенде Basic Auth не настроен, но он по сути и не нужен: либо работает как надо, либо отшибается еще до отправки запроса.
  • Вопрос задан
  • 7064 просмотра
Решения вопроса 1
merlin-vrn
@merlin-vrn
В общем, я склонен считать, что это баг в jQuery. Я написал код для CORS с Basic Auth «руками» без jQuery, он работает как минимум в Fx 10 и 17 и в Opera 12.10

Код следующий:
на клиенте —

<!DOCTYPE html>
<html><head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>CORS testing</title>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function() {
    document.getElementById("1").addEventListener("click", function() {
        var xhr = new XMLHttpRequest();
        xhr.onload = function(e) {
            document.getElementById("2").textContent = xhr.responseText;
        };
        xhr.open("GET", "http://dev.merlin-vrn.tk/cors/api.php", true);

//        xhr.withCredentials = true;
        xhr.setRequestHeader("Authorization", "Basic dGVzdHVzZXI6dGVzdHBhc3N3b3Jk");

        xhr.send();
    }, false);
}, false);
</script>

</head><body>

<button id="1">Click me</button>
<div id="2"></div>

</body></html>



на севрере —
<?php

if (isset($_SERVER['HTTP_ORIGIN']))
    header("Access-Control-Allow-Origin: $_SERVER[HTTP_ORIGIN]");

if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {                                                                                                                        
    if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))                                                                                                        
        header("Access-Control-Allow-Methods: $_SERVER[HTTP_ACCESS_CONTROL_REQUEST_METHOD]");                                                                         
                                                                                                                                                                      
    if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))                                                                                                       
        header("Access-Control-Allow-Headers: $_SERVER[HTTP_ACCESS_CONTROL_REQUEST_HEADERS]");                                                                        
} else {                                                                                                                                                              
    header('WWW-Authenticate: Basic realm="API"');                                                                                                                    

    header("Content-Type: application/json");

    if (isset($_SERVER['PHP_AUTH_USER']) && $_SERVER['PHP_AUTH_USER'] == "testuser" && isset($_SERVER['PHP_AUTH_PW']) && $_SERVER['PHP_AUTH_PW'] == "testpassword") {
        header('HTTP/1.1 200 OK');
        echo '{"result": "ok"}';
    } else {
        echo '{"result": "noauth"}';
    }
}

file_put_contents("api-log.txt", "$_SERVER[REQUEST_METHOD] $_SERVER[REQUEST_URI] $_SERVER[SERVER_PROTOCOL]\n", FILE_APPEND);
file_put_contents("api-log.txt", print_r($GLOBALS, true), FILE_APPEND);
file_put_contents("api-log.txt", "\n\n", FILE_APPEND);

?>



Логирование в конце этого скрипта — просто для удобства при тестировании. Живьём можно видеть здесь.

P.S. господа, подскажите хайлайтер для хабра, а?
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@quozd
Для IE необходимо указать
$.support.cors = true;


Иначе работать не будет, по поводу Оперы ничего не скажу, ибо не пробовал.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы