理解跨域之前,需要先理解什么是跨域。
跨域是指一个网页的脚本试图去请求来自不同源(域名、协议或端口)的资源。
在网络应用中,源的定义包括协议(如 http
或 https
)、域名(如 example.com
或 anotherdomain.com
)和端口(如 80
或 8080
)。如果这三个要素中的任何一个不同,就被认为是不同的源。
再来理解一个概念: 同源策略
同源策略的主要目的是防止不同源的网页之间未经授权的交互,从而保护用户的隐私和安全。
比如,假设没有同源策略的限制,恶意网站可能会通过 JavaScript 访问您在其他网站上的敏感信息,如银行账户信息、个人聊天记录等。再举个例子,如果您正在浏览一个正常的购物网站,而同时有一个恶意网站能够突破同源策略,那么它就有可能获取您在购物网站中的登录凭证、购物车内容等信息,这将给您带来极大的安全风险。同源策略虽然保障了安全,但在某些情况下也给开发带来了一些挑战,比如跨域数据获取、前端与后端部署在不同源的情况等。这时就需要采用一些合法合规的技术手段,如 CORS 等来解决跨域问题。
这俩个概念合并在一起,给我们开发造成了很多麻烦。比如我在本地随便开了个端口,888端口,要远程调用服务器。如果直接请求,系统会提示,跨域失败。默认的报错一般是:Access to XMLHttpRequest at ‘xxxxxx’ from origin ‘http://127.0.0.1:888’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource。
为了绕过同源策略,于是我们需要各种操作。请求的时候,可能使用jsonp
理解下jsonp是怎样绕过同源策略限定:
<script>
标签具有一个特殊的特性,它可以从不同的源加载脚本(这个也是我们日常使用script从其他网站加载代码到本地),并且不会受到同源策略的限制。JSONP 正是利用了这一特点。在 JSONP 中,服务端返回的不是标准的 JSON 数据,而是一段 JavaScript 代码。这段代码通常是一个函数调用,函数名是在请求中指定的,而参数就是要返回的数据。
例如,客户端请求一个 JSONP 资源时,会在 URL 中指定一个回调函数的名称,比如:http://example.com/data?callback=myFunction
。服务端接收到这个请求后,返回类似这样的 JavaScript 代码:myFunction({ "key": "value" })
。
当浏览器加载这个脚本时,就会执行这个函数调用,从而在客户端获取到了来自不同源的数据。JSONP 绕过同源策略的关键在于它利用了 <script>
标签加载外部脚本的能力,并且将数据以函数调用的形式传递给客户端,而不是直接传递 JSON 数据。需要注意的是,JSONP 存在一些安全风险,比如可能会受到跨站请求伪造(CSRF)攻击,而且它只支持 GET 请求等。
也就是正常情况下,不能直接使用XHR类似的对象直接远程请求不同域的网址,被限定在正常的网址以内。而前端还可以使用类似whistle在本地反向代理的模式,完成类似跨域效果。先请求当前的域,拿到数据和远程请求,通过建议服务器端发送请求,这样也绕过了跨域限制。
而在实际的开发里面,除了客户端能配置跨域请求之外,服务器端同样也可以限定。
服务器端对跨域的放开限定:
header(“Access-Control-Allow-Origin: *”);
该指令在php里面表示不限定跨域,允许所有的网址直接调用访问网站的接口。有该行代码的系统(一般情况下,服务端是不允许放开限定的),任意网站的JS脚本都可以访问该网站。
更高级的使用,一般除了主站之外,还希望我们购买的其他域名也能直接跨域访问,需要对应脚本限定。这样我们自己网站都允许访问。很多图片的访问,也是通过这种模式限定的。
$allowed_suffixes = array('xx.com', 'xx.org', 'xxx.net'); // 定义允许的域名后缀
$origin = $_SERVER['HTTP_ORIGIN']?? '';
foreach ($allowed_suffixes as $suffix) {
if (str_ends_with($origin, $suffix)) {
header('Access-Control-Allow-Origin: '. $origin);
break;
}
}
header(“Access-Control-Allow-Headers: token,Content-Type,Authorization”); 跨域请求头部的放开,跨域的时候,头部的参数请求是被严格限定的。内容跨域和头部里面参数都会经过严格的校验,如果跨域的时候,需要让服务器能直接获取到参数并进行操作,需要对请求头里面的信息也进行放开。这有个好处,如果传递某个公共的cookie参数,跨域之后,cookie是无法读取的,很多依赖cookie进行认证的身份操作,会直接失效,这个时候依赖header认证的操作,需要在远程服务器的请求里面新增一个参数。