使用 ajax 发出 http 请求以获得基本授权和 cors

Posted

技术标签:

【中文标题】使用 ajax 发出 http 请求以获得基本授权和 cors【英文标题】:Make a http request with ajax for basic authorization and cors 【发布时间】:2019-02-07 02:39:11 【问题描述】:

我的 httpd 服务器 111.111.111.111 的配置(假设)。 在 /etc/httpd/conf/httpd.conf 中配置 cors 和基本身份验证。

<Directory "/var/www/html">
    Options Indexes FollowSymLinks
    AllowOverride AuthConfig
    Require all granted
    Header always set Access-Control-Allow-Origin "*"
    Header always set Access-Control-Allow-Methods "POST, GET, PUT, DELETE, OPTIONS"
    Header always set Access-Control-Allow-Credentials "true"
    Header always set Access-Control-Allow-Headers "Authorization,DNT,User-Agent,Keep-Alive,Content-Type,accept,origin,X-Requested-With"
</Directory>

在我的服务器 111.111.111.111 上进行更多基本授权配置。

cd /var/www/html && vim .htaccess
AuthName "login"  
AuthType Basic  
AuthUserFile /var/www/html/passwd  
require user username 

为用户名创建密码。

htpasswd -c /var/www/html/passwd username

用 :

重启 httpd
systemctl restart httpd

服务器上的/var/www/html/remote.html 111.111.111.111

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="Access-Control-Allow-Origin" content="*" />
</head>
<body>
<p>it is a test </p>
</body>
</html>

使用用户名和密码测试它何时在浏览器中打开111.111.111.111\remote.html?username=xxxx&amp;password=xxxx

it is a test

使用 curl 获取响应头。

curl -u xxxx:xxxx -I  http://111.111.111.111/remote.html
HTTP/1.1 200 OK
Date: Thu, 06 Sep 2018 00:59:56 GMT
Server: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips php/5.4.16
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Authorization,X-PINGOTHER,DNT,User-Agent,Keep-Alive,Content-Type,accept,origin,X-Requested-With
Last-Modified: Wed, 05 Sep 2018 15:01:05 GMT
ETag: "f5-575210b30e3cb"
Accept-Ranges: bytes
Content-Length: 245
Content-Type: text/html; charset=UTF-8

在header中添加参数OPTIONS

curl -X OPTIONS -i http://111.111.111.111/remote.html

HTTP/1.1 401 Unauthorized
Date: Thu, 06 Sep 2018 06:42:04 GMT
Server: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/5.4.16
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Authorization,X-PINGOTHER,DNT,User-Agent,Keep-Alive,Content-Type,accept,origin,X-Requested-With
WWW-Authenticate: Basic realm="please login"
Content-Length: 381
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>401 Unauthorized</title>
</head><body>
<h1>Unauthorized</h1>
<p>This server could not verify that you
are authorized to access the document
requested.  Either you supplied the wrong
credentials (e.g., bad password), or your
browser doesn't understand how to supply
the credentials required.</p>
</body></html>

在标题中添加OPTIONS 和基本授权。

curl -X OPTIONS -u xxxx:xxxxx  -i http://111.111.111.111/remote.html

HTTP/1.1 200 OK
Date: Thu, 06 Sep 2018 06:42:54 GMT
Server: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/5.4.16
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Authorization,X-PINGOTHER,DNT,User-Agent,Keep-Alive,Content-Type,accept,origin,X-Requested-With
Allow: POST,OPTIONS,GET,HEAD,TRACE
Content-Length: 0
Content-Type: text/html; charset=UTF-8

好的,一切都很好。 让我们试试ajax的基本授权。

我本地 apache 上的 /var/www/html/test.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <script src="http://127.0.0.1/jquery-3.3.1.js"></script>
    <script>
    function Ajax( ) 
        var url = 'http://111.111.111.111/remote.html';
        $.ajax(url, 
            type:"get",
            dataType: 'html',
            withCredentials: true,
            username: "xxxx",
            password: "xxxx",
            success:function(response)
                mytext = $("#remote");
                mytext.append(response);
            ,
            error: function (e) 
                alert("error");
                
        );
    ;
    </script>

    <input type="button" value="show content" onclick="Ajax();">
    <p id="remote">the content on remote webpage</p>
</body>
</html>

在输入127.0.0.1/test.html时点击show content按钮,出现错误:

GET http://111.111.111.111/remote.html 401 (Unauthorized)

我已经根据httpd设置(centos7)和ajax等相关问题进行了详细描述,请下载我的代码并保存在你的vps和本地htdocs目录中,将ip替换成你的真实ip,重现过程. 我请求您在重现该过程之前不要制作任何 cmets。 你可能会发现发生了什么,也许和我这里的一样。

问题中的两个重要元素。 1./etc/httpd/conf/httpd.conf文件中的httpd设置。 2.ajax代码 哪个错了? 如何解决?

感谢@sideshowbarker,我有一些解决问题的线索。

reason

问题变成了另一个问题: 如何将 apache 配置为不需要对 OPTIONS 请求进行授权? 我试过Disable authentication for HTTP OPTIONS method (preflight request) 说。

Disable authentication for HTTP OPTIONS method (preflight request)e

<Directory "/var/www/html">
<LimitExcept OPTIONS>
  Require valid-user
</LimitExcept>
</Directory>

systemctl restart httpd,失败。

【问题讨论】:

当您尝试curl -X OPTIONS -i http://111.111.111.111/remote.html 时会发生什么? 请查看我修改后的帖子,并按照我在您的 vps 中说的做。 将凭据添加到 OPTIONS 请求的问题是,浏览器在执行 CORS 预检时不会添加它们。请参阅fetch.spec.whatwg.org/#ref-for-credentials%E2%91%A5(“CORS 预检请求从不包含凭据”)。代替预检,浏览器发送一个没有任何 Authorization 标头的 OPTIONS 请求。这意味着接收请求的服务器必须配置为接受 OPTIONS 请求而不需要身份验证,并且必须以 200 OK 响应那些未经身份验证的 OPTIONS 请求。否则预检失败。这就是你遇到的问题 另见答案***.com/questions/45405983/… 我想这并不是您问题的直接答案,但您确实不应该通过 HTTP 发送明文密码作为 GET 请求。 【参考方案1】:

如果您console.log 错误,您将看到一条消息:

无法加载http://111.111.111.111/remote.html:响应中'Access-Control-Allow-Origin'标头的值不能是通配符'*'时请求的凭据模式为“包含”。因此,Origin 'http://127.0.0.1/' 不允许访问。 XMLHttpRequest发起的请求的凭证模式由withCredentials属性控制。

这是为了防止 CSRF 攻击

例如如果在这种情况下可以使用通配符:Malicious-SiteA 可以通过浏览器发起的 ajax 请求访问特权资源未经授权在 target-SiteB 上/strong>,只是因为受害者在某个时候提供了SiteB 的凭据,而他在受信任的SiteC 上。

所以解决方案就是将Access-Control-Allow-Origin从“*”改为“http://127.0.0.1”,(上例中的SiteC地址)


现在,为了让curl -X OPTIONS -i http://111.111.111.111/remote.html 工作,同时保持其他方法的有效身份验证,您需要将以下内容添加到.htaccesshttpd.conf

<LimitExcept OPTIONS>
   AuthName "login"
   AuthType Basic
   AuthBasicProvider file
   AuthUserFile /var/www/cors-auth.passwd
   Require user username
 </LimitExcept>

编辑:

LimitExcept OPTIONS如果您需要预先填写用户名/密码(如您的情况),而不是期望浏览器提示对话框。但要使其适用于所有浏览器(chrome/ff/edge), 我不得不替换这个:

        withCredentials: true,
        username: "xxxx",
        password: "xxxx",

用这个:

      beforeSend: function (xhr) 
           xhr.setRequestHeader('Authorization', "Basic " + btoa("xxxx:xxxx")); 
       ,

【讨论】:

【参考方案2】:

尝试使用 localhost 而不是 'http://111.111.111.111/remote.html'

'http://localhost/remote.html 应该可以了

【讨论】:

但是没有必要使用 CORS 标头。问题是关于使用授权+cors,这意味着主页和ajax部分应该属于不同的域。 Cors 必须固定在请求的 url 上。如果您正在对远程服务器/asd.php 进行 ajax 调用,那么您必须在远程服务器上打开课程。在他的情况下,他只是将远程服务器和本地服务器复制为 111.111.111.111,因此没有来自 111.111.111.111 的 cors到 111.111.111.111。

以上是关于使用 ajax 发出 http 请求以获得基本授权和 cors的主要内容,如果未能解决你的问题,请参考以下文章

AngularJs 获得授权请求

使用 Javascript 发出 HTTP POST 身份验证基本请求

带有 php 代理的 AJAX 状态代码 0

用于基本授权的 ajax 设置标头不起作用

通过 HTTPS 的 HTTP Cookie 和 Ajax 请求

UAA与Oathor