likes
comments
collection
share

跨域问题及其解决方案

作者站长头像
站长
· 阅读数 48

跨域问题及其解决方案

本文介绍了跨域问题的定义和同源策略的限制,以及三种跨域解决方案:CORS、JSONP和代理。

什么是跨域问题

我们先看一段简单的html代码,对所谓的跨域问题有一个直观的了解

<!DOCTYPE html>
<html>
<head>
	<title>跨域问题示例</title>
	<meta charset="utf-8">
	<script>
		function requestData() {
			var xhr = new XMLHttpRequest();
			xhr.onreadystatechange = function() {
				if (xhr.readyState == 4 && xhr.status == 200) {
					document.getElementById("result").innerHTML = xhr.responseText;
				}
			}
			xhr.open("GET", "<https://www.baidu.com/>", true);
			xhr.send();
		}
	</script>
</head>
<body>
	<button onclick="requestData()">请求百度</button>
	<div id="result"></div>
</body>
</html>

页面上的按钮会触发一个JavaScript函数,该函数使用XMLHttpRequest对象向www.baidu.com/发出GET请求。如果请…

但是,该请求将会被浏览器拦截。在控制台中,我们会看到以下错误消息:

跨域问题及其解决方案

Access to XMLHttpRequest at '<https://www.baidu.com/>' from origin 'null' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.

这就是我们常说的跨域问题的具体表现之一。

同源政策

跨域错误源自浏览器的”同源政策”(same-origin policy),该策略在浏览器中的实现最初由 Netscape Communications Corporation 在1995年引入。

同源策略是浏览器的基本安全策略,旨在防止恶意网站窃取用户数据,缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击

这里的同源指的是两个页面的协议、端口和主机名都相同,例如

请求1请求2是否同源
www.example.com/dir/page.ht…www.example.com/dir/page.ht…同源
www.example.com/dir/page.ht…www.example.com/dir/page.ht…不同源

同源策略主要限制以下行为:

  1. Cookie、LocalStorage 和 IndexDB 等存储机制的访问:同源的页面可以共享这些存储机制,但不同源的页面无法访问对方的存储数据。
  2. DOM 节点的访问:同源的页面可以通过 JavaScript 访问对方的 DOM 节点,但不同源的页面无法访问对方的 DOM 节点。
  3. AJAX 请求的发送:同源的页面可以通过 AJAX 发送请求并获取响应,但不同源的页面无法发送请求或获取响应。
  4. 通过 iframe 加载的资源的访问:如果一个页面通过 iframe 加载了另一个页面,那么这两个页面必须同源才能相互访问。
  5. Web Workers 之间的消息通信:同源的 Web Workers 可以相互通信,但不同源的 Web Workers 无法通信。

在远古时代,大部分应用都是单体应用,同源策略并不会造成什么困扰,直到前后端分离开发模式的出现。在前后端分离的开发模式下,前端服务往往部署在A域名下,后端服务往往部署在B域名下,这时前端请求后端接口,就会出现我们一开始例子中的跨域问题。

解决方案

CORS(跨域资源共享)

CORS 是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。

该机制解决跨域问题的方式如下:在浏览器发出跨域请求前,要求后端服务器先列出允许跨域访问的源,浏览器根据这个源名单放行跨域请求。具体实现上,浏览器在发出正式请求前,增加一次OPTIONS请求,称为"预检"请求(preflight)。

跨域问题及其解决方案 这里以Springboot项目为例,例举两种对预检请求的处理方式

  • 在过滤器中处理预检请求
@Configuration
public class MyWebConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
								// allowedOrigins 决定哪些域名可以跨域发起请求 这里*表示放行所有跨域请求
                .allowedOrigins("***")
                .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
                .maxAge(3600)
                .allowCredentials(true);
    }
}
  • 使用@CrossOrigin 注解
  @GetMapping(value = "login")
  @CrossOrigin
  public Map<String, Object> queryActivityBonusInfo() {
        .......
  }

JSONP

JSONP是一种跨域请求的解决方案,它的原理是利用script标签的src属性不受同源策略限制的特点,通过动态创建script标签的方式向服务器发送请求,服务器返回的数据被包裹在一个函数调用中返回给客户端,客户端通过回调函数获取数据。

<script>
function handleData(data) {
  console.log(data);
}

const url = '<http://example.com/data?callback=handleData>';
const script = document.createElement('script');
script.src = url;
document.body.appendChild(script);
</script>

在这个例子中,我们定义了一个名为handleData的回调函数来处理从服务器返回的数据。

我们使用document.createElement()方法创建了一个<script>元素,并将其src属性设置为带有回调函数名称的URL。

然后,我们将该元素添加到文档中,浏览器会自动执行该URL返回的JavaScript代码,该代码将调用我们定义的回调函数,并将数据作为参数传递给该函数。

服务器必须返回一个包含回调函数名称和数据的JavaScript函数,例如:

handleData({"name": "John", "age": 30});

代理

代理是一种比较常见的跨域解决方案,它的原理是通过在同域下的服务器端发起请求,将请求转发给目标服务器,再将目标服务器返回的数据返回给客户端,客户端只需要与同域下的服务器端进行通信即可。

使用服务器代理

可以通过设置服务器代理来解决跨域问题。例如,使用 Node.js 的 Express 框架:

const express = require('express');
const request = require('request');

const app = express();

app.get('/api', (req, res) => {
  const url = '{要请求的接口地址}';
  req.pipe(request(url)).pipe(res);
});

app.listen(3000, () => {
  console.log('服务器已启动');
});

在上述代码中,我们定义了一个 /api 路由,当我们访问该路由时,会使用 request 模块向指定接口地址发起请求,并将请求结果返回给前端。

使用反向代理

另一种常见的代理方式是使用反向代理。例如,使用 Nginx:

server {
  listen 80;
  server_name {服务器地址};

  location /api {
    proxy_pass {要请求的接口地址};
  }
}

在上述代码中,我们定义了一个 /api 路由,当我们访问该路由时,Nginx 会将请求转发给指定的接口地址,并将请求结果返回给前端。

转载自:https://juejin.cn/post/7236305021594566693
评论
请登录