11# 万字长文带你理清楚前端跨域所有解决方案的细节
22
3- 前端时间chrome更新了204版本之后 ,由于新版的chrome把iframe 放在了独立的进程渲染,导致公司的的一个angular子应用使用iframe嵌套在主应用中发生了跨域,心想在解决问题的时候顺便把跨域相关的知识全部整理一遍吧
3+ > 前段时间chrome更新了204版本之后 ,由于新版的chrome把iframe 放在了独立的进程渲染,导致公司的的一个angular子应用使用iframe嵌套在主应用中发生了跨域,心想在解决问题的时候顺便把跨域相关的知识全部整理一遍吧
44
55## 一、什么是跨域?
66
@@ -27,11 +27,11 @@ https://www.example.com:443/api/posts ✓ 同源(只是路径不同)
2727
2828### 同源策略限制了什么?
2929
30- 在我的开发经验中, 同源策略主要限制以下几个方面:
30+ 同源策略主要限制以下几个方面:
3131
32- 1 . ** Ajax 请求** :不能向不同源的服务器发送请求
33- 2 . ** DOM 访问** :不能访问跨域 iframe 的 DOM
34- 3 . ** Cookie/LocalStorage/IndexDB** :不能读取跨域的存储数据
32+ 1 . ** Ajax 请求** : 可以向不同源服务器发起请求,但浏览器会限制 JavaScript 代码读取非同源响应的内容(比如无法访问 response 数据)。也就是说,跨域的 Ajax 请求可以发出,但响应会被同源策略拦截,页面拿不到数据。这就是常见的“跨域请求被阻止”问题。
33+ 2 . ** DOM 访问** : 不能访问跨域 iframe 的 DOM
34+ 3 . ** Cookie/LocalStorage/IndexDB** : 不能读取跨域的存储数据
3535
3636``` mermaid
3737graph TD
@@ -67,14 +67,6 @@ fetch("https://api.example.com/users", {
6767 .then ((response ) => response .json ())
6868 .then ((data ) => console .log (data))
6969 .catch ((error ) => console .error (" Error:" , error));
70-
71- // 或使用 axios
72- axios
73- .post (" https://api.example.com/users" , {
74- name: " John" ,
75- })
76- .then ((response ) => console .log (response .data ))
77- .catch ((error ) => console .error (" Error:" , error));
7870```
7971
8072** 后端代码** (Node.js + Express):
@@ -123,43 +115,6 @@ app.post("/users", (req, res) => {
123115app .listen (3000 );
124116```
125117
126- ** 使用 cors 中间件** (推荐):
127-
128- ``` javascript
129- const express = require (" express" );
130- const cors = require (" cors" );
131- const app = express ();
132-
133- // 简单配置
134- app .use (cors ());
135-
136- // 详细配置
137- app .use (
138- cors ({
139- origin: [" https://www.mysite.com" , " https://app.mysite.com" ],
140- methods: [" GET" , " POST" , " PUT" , " DELETE" ],
141- allowedHeaders: [" Content-Type" , " Authorization" ],
142- credentials: true , // 允许携带 Cookie
143- maxAge: 86400 , // 预检请求缓存 24 小时
144- })
145- );
146-
147- // 或者动态配置
148- app .use (
149- cors ({
150- origin : function (origin , callback ) {
151- const whitelist = [" https://www.mysite.com" , " https://app.mysite.com" ];
152- if (! origin || whitelist .includes (origin)) {
153- callback (null , true );
154- } else {
155- callback (new Error (" Not allowed by CORS" ));
156- }
157- },
158- credentials: true ,
159- })
160- );
161- ```
162-
163118#### CORS 的两种请求类型
164119
165120在使用 CORS 时,我发现请求分为两种类型:
@@ -212,8 +167,6 @@ Access-Control-Request-Headers: Content-Type
212167
213168### 方案二: JSONP ⭐⭐
214169
215- JSONP 是我刚开始工作时常用的方案,现在已经很少用了,但了解它的原理还是很有帮助的。
216-
217170#### 原理说明
218171
219172JSONP 利用了 ` <script> ` 标签不受同源策略限制的特性。原理就是:
@@ -250,85 +203,6 @@ function jsonp(url, callbackName, callback) {
250203jsonp (" https://api.example.com/users" , " handleUserData" , function (data ) {
251204 console .log (" 收到数据:" , data);
252205});
253-
254- // 更完善的 JSONP 实现(支持 Promise)
255- function jsonpPromise (url , params = {}) {
256- return new Promise ((resolve , reject ) => {
257- // 生成唯一的回调函数名
258- const callbackName = ` jsonp_${ Date .now ()} _${ Math .random ().toString (36 ).substr (2 )} ` ;
259-
260- // 定义全局回调函数
261- window [callbackName] = function (data ) {
262- resolve (data);
263- cleanup ();
264- };
265-
266- // 创建 script 标签
267- const script = document .createElement (" script" );
268-
269- // 拼接 URL 参数
270- const queryString = Object .keys (params)
271- .map ((key ) => ` ${ key} =${ encodeURIComponent (params[key])} ` )
272- .join (" &" );
273-
274- script .src = ` ${ url} ?callback=${ callbackName}${ queryString ? " &" + queryString : " " } ` ;
275-
276- // 错误处理
277- script .onerror = function () {
278- reject (new Error (" JSONP request failed" ));
279- cleanup ();
280- };
281-
282- // 超时处理
283- const timeout = setTimeout (() => {
284- reject (new Error (" JSONP request timeout" ));
285- cleanup ();
286- }, 10000 );
287-
288- function cleanup () {
289- clearTimeout (timeout);
290- if (script .parentNode ) {
291- document .body .removeChild (script);
292- }
293- delete window [callbackName];
294- }
295-
296- document .body .appendChild (script);
297- });
298- }
299-
300- // 使用 Promise 版本
301- jsonpPromise (" https://api.example.com/users" , { page: 1 , limit: 10 })
302- .then ((data ) => console .log (" 用户列表:" , data))
303- .catch ((error ) => console .error (" 请求失败:" , error));
304- ```
305-
306- ** 后端代码** (Node.js):
307-
308- ``` javascript
309- const express = require (" express" );
310- const app = express ();
311-
312- app .get (" /users" , (req , res ) => {
313- const callbackName = req .query .callback ;
314-
315- if (! callbackName) {
316- return res .status (400 ).json ({ error: " Missing callback parameter" });
317- }
318-
319- const data = {
320- users: [
321- { id: 1 , name: " John" },
322- { id: 2 , name: " Jane" },
323- ],
324- };
325-
326- // 返回 JavaScript 代码,调用回调函数
327- res .type (" application/javascript" );
328- res .send (` ${ callbackName} (${ JSON .stringify (data)} )` );
329- });
330-
331- app .listen (3000 );
332206```
333207
334208#### 优劣势分析
0 commit comments