主题
同源策略与跨域
本页对应 同源策略与跨域,共 4 段视频,约 37 分钟。
学习目标
- 能准确说明浏览器同源策略的作用,并依据协议、域名、端口号判断两个 URL 是否同源。
- 能识别并解释跨域问题,能根据浏览器控制台报错信息判断请求是否因跨域失败。
- 能在 Express 后端项目中安装并配置 CORS,验证响应头包含
Access-Control-Allow-Origin且前端可正常获取数据。 - 能区分 CORS 与同源访问方案的适用场景,说明开发阶段与上线部署阶段应如何选择。
- 能使用 Express 的
static方法暴露前端静态资源目录,验证网页与其内嵌 Ajax 请求同源并可正常访问。
知识点
本节目标与同源策略
本节目标是:掌握跨域的概念,以及出现跨域问题时浏览器会怎样报错、我们怎样复现和解决。
为什么需要:前后端分离开发时,前端页面常由 Live Server 启动在 http://localhost:5500,后端接口却运行在 http://localhost:3000。浏览器有同源策略,会限制一个源的文档或脚本与另一个源进行数据交互,尤其是 AJAX/XHR 请求。
怎么想:先记住“源”的组成:协议、域名、端口号。所谓同源,就是网页打开时所在源的 URL 与 AJAX 请求的 URL 中,协议、域名、端口号全部一致。转写稿里用钓鱼网站举例:页面源和 AJAX 请求源域名不同,就不是同源,浏览器会限制请求响应。这里不要把“源”写成“圆”。
怎么做:判断跨域时只看三元组,不看资源路径。例如 http://localhost:5500 与 http://localhost:3000/api/province,协议和域名相同,但端口 5500 与 3000 不同,所以跨域。
易错点:端口不同也算跨域;资源路径 /api/province 不参与同源判断;同源策略是浏览器行为,不是服务器“坏了”。
跨域是怎么发生的
为什么需要:需要先能识别跨域。跨域是“从一个源的文档/脚本,加载另一个源的资源”时产生的问题。网页文档打开时所在源和 AJAX 访问的源,只要协议、域名、端口有一个不同,就发生跨域访问。

怎么想:用本节需求复现:在 Live Server 的 Web 服务启动网页,用 AJAX 访问本机 Web 服务提供的省份列表接口。Live Server 通常启动在 5500,本机接口在 3000,端口不同,因此会跨域。
怎么做:index.html 中的请求代码如下:
js
// 目标:请求本机 Web 服务提供的省份列表数据
axios({
url: 'http://localhost:3000/api/province',
// method: 'get'
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})调试时先启动 3000 的后端服务,用浏览器地址栏访问接口确认服务正常;再用 Live Server 打开前端页面,按 F12 打开开发者工具。控制台会出现类似报错:
Access to XMLHttpRequest 意思为:XHR 链接出了问题,从一个源(http://localhost:5500 跨域访问 http://localhost:3000)

易错点:不要只看到 axios 的 catch 就以为接口写错;先看 Console 是否有 Access to XMLHttpRequest 这类跨域报错。转写稿强调,判断跨域时不需要考虑资源路径,因为源里只有协议、域名、端口号。
用 CORS 解决开发阶段跨域
为什么需要:前后端分离项目里,前端和后端代码不在同一个源下,但数据通信还要正常进行。开发阶段常用 CORS(跨域资源共享)解决。
怎么想:CORS 是基于 HTTP 头的机制,核心是让服务器端设置响应头字段 Access-Control-Allow-Origin,标记允许其他源访问服务器资源。前端不需要额外操作,仍正常用 axios 发请求、接收响应。
怎么做:后端先下载固定名字的 cors 软件包,再导入函数,并把它交给 Express 的 Web 服务对象:
bash
npm i corsjs
const cors = require('cors')
server.use(cors())转写稿中的调试过程是:启动 3000 后端,在浏览器打开接口并按 F12 查看 Network 响应头,能看到 Access-Control-Allow-Origin 字段;再用 Live Server 从 5500 打开前端,Console 中原来的跨域报错消失,axios 能拿到省份列表数据。
易错点:CORS 主要是后端设置,不是前端改 axios;npm 用来下载包,node 用来执行文件,不要混;开发阶段用 CORS,不代表上线后可以无脑开放所有源。
上线部署:同源访问
为什么需要:转写稿提出一个关键问题:如果上线后后端仍开启 CORS,攻击者的钓鱼网站也可能来访问服务器,存在风险。因此上线部署时通常关闭 CORS,改为同源访问。
怎么想:让后端 Web 服务既提供数据接口,也返回网页静态资源。这样浏览器访问 http://localhost:3000/index.html,页面里的 AJAX 也请求 http://localhost:3000/api/province,网页所在源和 AJAX 请求源一致,就是同源访问。
怎么做:用 Express 内置的 static 方法暴露前端文件夹,转写稿强调路径用绝对路径更严谨:
js
server.use(express.static(path.join(__dirname, 'public')))把前端页面放入 public 文件夹,启动后端后访问 http://localhost:3000/index.html。此时即使后端没有开启 CORS,页面内 AJAX 也能正常拿到数据,因为已经同源。
易错点:同源访问主要用于上线环境;本地开发仍常用 CORS。不要再用 Live Server 的 5500 打开页面去请求 3000 接口,否则又变成跨域。static 暴露的是指定文件夹下的资源,路径不要写错。
小结与自测
什么是跨域访问?网页所在源和 AJAX 请求的源(协议、域名、端口号)不完全相同,就发生跨域访问。开发阶段可用 CORS,让后端在响应头中允许跨域;上线部署可把前端静态资源放到后端 Web 服务下,让页面和接口同源。遇到报错时,先看 Console 和 Network,再判断两个源的三元组是否一致。
先理解:为什么会出现跨域问题
为什么需要:前后端分离项目中,前端页面和后端接口常常不在同一个“源”下。浏览器有同源策略,它会限制一个源的文档或脚本与另一个源的资源进行数据交互。转写稿里举了钓鱼网站的例子:攻击者页面里的 AJAX 想把用户填写的信息请求到真正银行服务器,浏览器会用同源策略限制这种跨源请求,从而降低风险。
怎么想:判断是否同源,只看 URL 中的协议、域名、端口号,三者全部一致才是同源;资源路径不参与同源判断。比如页面在 http://localhost:5500,AJAX 请求 http://localhost:3000/api/province,端口不同,就是跨域。转写稿中在浏览器控制台看到类似 Access to XMLHttpRequest ... has been blocked by CORS policy 的报错,就是跨域被浏览器拦截了。
怎么做:先明确“跨域不是请求没发出去,而是浏览器基于同源策略不让前端脚本读取响应”。开发阶段要解决它,让前后端能正常通信。
易错点:不要以为路径不同就一定跨域;真正要看协议、域名、端口。也不要以为换一个接口路径就能绕过同源策略。
CORS 的思路:让服务器声明允许谁来访问
为什么需要:前后端分离时,前端在 localhost:5500 开发,后端在 localhost:3000 提供接口,二者不同源,浏览器会拦截 AJAX 响应,省份列表等数据就拿不到。
怎么想:采用 CORS(跨域资源共享)。它是一种基于 HTTP 头的机制:服务器通过响应头标明,除了自己以外,哪些源可以访问自己的资源。核心响应头是 Access-Control-Allow-Origin。前端仍然正常发起 AJAX 请求,不需要额外操作;关键是后端在响应时加上允许跨域的响应头。

怎么做:在 Express 搭建的 Web 服务中引入 cors 中间件,让服务在响应时自动添加相关响应头。笔记中的核心代码是:
js
// 2. 导入 cors 函数
const cors = require('cors')
// 3. 使用 server.use() 给 Web 服务添加插件功能
server.use(cors())易错点:CORS 主要由后端配置,不是前端在 Axios 里“加一个参数”就完成;cors 是软件包名,不要写成 cars、cse 等同音错词;server.use(cors()) 中 cors 要调用,传入的是调用后的结果。
后端接入 cors 的步骤与调试过程
为什么需要:只理解概念不够,要把后端服务真正开启 CORS,才能验证跨域问题是否解决。
怎么想:按“下载包 → 导入函数 → 注册到 Web 服务 → 启动测试”的顺序做。转写稿中先启动 3000 端口后端,用浏览器地址栏访问接口,确认后端本身没问题;再用 Live Server 在 5500 端口打开前端页面,此时 Axios 请求 3000 端口接口,控制台出现跨域报错。接着给后端加 cors,重新启动服务,再刷新前端页面,报错消失,并拿到省份列表数据。
怎么做:
- 在后端项目目录下执行
npm i cors下载本地软件包; - 在
server.js中导入:const cors = require('cors'); - 通过
server.use(cors())给 Web 服务添加插件功能; - 启动后端服务,打开浏览器开发者工具的 Network 面板查看响应头,应能看到
Access-Control-Allow-Origin字段;再回到前端页面刷新,确认控制台跨域报错消失、数据正常返回。
易错点:下载包要在后端项目目录中执行;改完后端代码要重启服务;调试时先看控制台报错,再看 Network 的响应头,不要只盯着前端代码。CORS 解决的是开发阶段前后端分离的跨域通信问题,前端仍然按正常方式使用 Axios 发请求即可。
本节目标:上线后为什么改用同源访问
为什么需要:开发阶段前后端分离,前端常由 Live Server 在 5500 端口打开,后端接口在 3000 端口,端口不同就会跨域。开发时可以用 CORS 解决,让后端通过响应头允许其他源访问。但项目上线后,如果仍开启 CORS,攻击者的钓鱼网站里也能用 AJAX 请求后端接口,存在安全风险。
怎么想:CORS 适合本地开发阶段;上线部署时关闭 CORS,改为同源访问,让浏览器打开网页的源和 AJAX 请求接口的源完全一致,从源头上避免跨域。
怎么做:让后端 Web 服务既能提供数据接口,也能返回网页静态资源;把前端网页部署到后端服务下,浏览器直接访问后端服务返回的网页,网页里的 AJAX 再请求同一服务下的接口。
易错点:不要把 CORS 当成所有阶段通用方案;上线后不要继续任意源放行;同源判断只看协议、域名、端口,不看资源路径。
同源访问的核心:网页和接口在同一个源
为什么需要:要判断什么时候会跨域,必须先明确“源”的组成。转写稿中反复强调:源由协议、域名、端口号组成;网页所在源与 AJAX 请求的源三者全部一致,才算同源。
怎么想:例如浏览器访问 http://localhost:3000/index.html,页面内 AJAX 请求 http://localhost:3000/api/province,协议、域名、端口都相同,所以是同源访问。若网页仍由 Live Server 的 http://localhost:5500 打开,再请求 http://localhost:3000/api/province,端口不同,仍然跨域。
怎么做:上线时把前端网页和后端接口放到同一个 Web 服务、同一个源下访问。后端既返回网页资源,又提供数据接口。
易错点:不要用资源路径判断同源;/api/province 和 /index.html 路径不同不影响同源判断;只要协议、域名、端口有一个不同,就是跨域。

让 Express 后端同时返回网页资源
为什么需要:同源访问要求网页也由后端 Web 服务返回。若后端只提供接口,不返回网页,浏览器仍可能从别的源打开页面,跨域问题依旧存在。
怎么想:Express 快速搭建的 Web 服务可以借助内置的 static 方法,把指定文件夹暴露为静态资源目录。浏览器通过“域名 + 端口 + 资源路径”就能访问该目录下的 HTML、JS 等资源。
怎么做:把前端写好的网页放进 public 文件夹,在 server.js 中暴露该目录:
js
// 暴露指定的文件夹,让前端可以直接拼接路径和资源名字来访问
server.use(express.static(path.join(__dirname, 'public')))这样后端服务既能提供接口,也能返回 public 下的网页资源。转写稿还提醒:代码中访问其他文件时,用 path.join(__dirname, 'public') 写绝对路径更严谨。
易错点:不要只写相对路径 'public';暴露的是 public 目录本身,访问其中 index.html 时 URL 通常是 /index.html,不要误以为一定要写 /public/index.html;路径拼接不要漏掉 __dirname。
调试与验证:关闭 CORS 后仍能拿到数据
为什么需要:同源访问不是只改概念,必须验证关闭 CORS 后页面和接口仍能正常通信。
怎么想:启动后端服务,用浏览器直接访问后端返回的网页;打开 F12,在 Network/Console 中观察 AJAX 请求是否成功。若网页源和接口源一致,即使没有开启 CORS,也能拿到数据。
怎么做:在终端执行 node server.js 启动后端;浏览器访问后端服务返回的 public/index.html;页面内 AJAX 请求同源接口,例如 /api/province;F12 刷新后查看响应数据。
易错点:如果仍用 Live Server 的 5500 打开网页,再请求 3000 接口,仍会跨域;若控制台报 Access to XMLHttpRequest 一类错误,先检查网页是不是从后端同源地址打开,以及后端服务是否已启动。
小结与自测
CORS 只适用于什么阶段的项目?
答案:本地开发阶段项目。项目上线,如何解决跨域问题?
答案:把前端项目和后端项目部署到同一个源下访问,采用同源访问方式;上线时关闭 CORS,让后端 Web 服务既提供数据接口,也返回网页资源,从而保证接口不被非同源访问。
ECMAScript 标准模块语法:默认导出/导入与按需导出/导入
为什么需要:项目拆成多个文件后,需要把变量、函数、对象从一个文件交给另一个文件使用;ES Module 提供了标准写法,避免把所有代码堆在一个文件里。
怎么想:先区分“只导出一个主功能”和“导出多个小功能”。默认导出适合一个模块一个主入口;按需导出适合工具函数集合。导入时名字要与导出方式匹配。
怎么做:默认导出用 export default,导入时可自定义名字;按需导出用 export,导入时用 { } 且名字对应。转写稿没有展开具体语法,这里只保留标准用法,不额外扩展未讲内容。
易错点:默认导入不要加 { };按需导入不要漏花括号;同一个模块可以既有默认导出又有按需导出,但导入写法要分开。不要把 CommonJS 的 require/module.exports 与 ES Module 混用在同一套写法里。
加载包文件夹时,加载的是什么文件
为什么需要:require('包名') 或导入包文件夹时,Node 需要知道入口文件是谁,否则不知道执行哪个文件。
怎么想:包文件夹不是“随便找第一个 js”。Node 会先看该文件夹下的 package.json,读取 main 字段指向的入口文件;如果没有明确入口,再按默认规则找 index.js 等。转写稿未涉及此细节,这里按通用规则说明,不编造具体项目文件。
怎么做:排查时先打开包目录看 package.json 的 main;再看入口文件是否存在。自己写包时,应在 package.json 中写清入口。
易错点:不要以为文件夹名就是文件名;不要忽略 package.json;入口路径写错时,报错往往不是“语法错”,而是“找不到模块”。
npm 软件包管理器相关命令和作用
为什么需要:项目依赖第三方包时,需要下载、记录、删除和运行脚本;npm 负责管理这些依赖。
怎么想:先初始化项目,再安装依赖;安装后会在项目下生成 node_modules,并在 package.json 中记录依赖。转写稿中明确出现的是下载 CORS 包:在项目文件夹终端执行 npm install cors,下载后 node_modules 中出现 cors 及其依赖。
怎么做:常用命令包括 npm init 初始化、npm install 包名 安装、npm install 按 package.json 安装全部依赖、npm uninstall 包名 卸载、npm run 脚本名 执行脚本。执行 JS 文件用 node 文件名,不是 npm;转写稿中“n moon”应纠正为 node。
易错点:npm 是包管理器,不是执行 JS 文件的命令;安装包要在项目目录终端执行;不要提交 node_modules,应提交 package.json 和锁文件。
浏览器同源策略:协议、域名、端口必须一致
为什么需要:防止来源不明的网页脚本随意读取另一个源的敏感数据。转写稿用钓鱼网站举例:钓鱼页面把用户填写的转账信息通过 Ajax 发往真正银行服务器,如果没有同源策略限制,风险很大。
怎么想:判断同源只看三件事:协议、域名、端口。路径、查询参数不参与同源判断。http://localhost:5500 与 http://localhost:3000 端口不同,不同源;http://icbcbc.com 与 http://icbc.com.cn 域名不同,也不同源。
怎么做:写请求前先比较网页所在源和目标接口源。三个都一致才是同源;任意一个不同就是跨域访问。
易错点:不要看资源路径判断同源;http 默认端口 80,https 默认 443;端口不同也算跨域。转写稿中的“圆”应统一纠正为“源”。
跨域问题与 CORS 解决方案
为什么需要:前后端分离项目中,前端常在 http://localhost:5500 由 Live Server 打开,后端接口在 http://localhost:3000,两者不同源,浏览器会限制 Ajax 响应。转写稿中控制台出现类似 Access to XMLHttpRequest ... 的报错,就是跨域被拦截。
怎么想:跨域是浏览器同源策略导致的;解决思路之一是让后端明确允许某些源访问,即 CORS(跨域资源共享)。它基于 HTTP 响应头机制,前端正常发 Ajax,后端设置响应头。
怎么做:后端安装并引入 cors 包,把 CORS 作为 Express 服务的插件使用。转写稿中的核心做法是:
js
const cors = require('cors');
server.use(cors());这样后端响应中会带上允许跨域访问的响应头字段。转写稿提到字段名为 Access-Control-Allow-Origin,作用是标记允许其他源访问服务器资源。前端不需要额外操作,仍用 axios/XHR 正常请求和接收响应。
易错点:CORS 主要在后端做,不是前端加个参数就行;开发阶段用 CORS 方便,但不能理解成“永久放开”;转写稿中的“cars/cos/cse”应纠正为 CORS。
上线部署:用同源访问替代开发期 CORS
为什么需要:转写稿提出一个关键问题:如果上线后后端仍开启 CORS,攻击者的钓鱼网站也可能请求接口。因此开发阶段可用 CORS,上线部署面对互联网时应关闭 CORS,改用同源访问。
怎么想:让后端 Web 服务既提供数据接口,也返回前端静态网页资源。这样网页所在源和 Ajax 请求接口源一致,就不会触发跨域限制,同时接口不再允许非同源访问,更安全。
怎么做:把前端打包后的网页放进后端可暴露的 public 文件夹,用 Express 的 static 方法暴露该文件夹。转写稿中的核心做法是:
js
server.use(express.static(path.join(__dirname, 'public')));启动后端后,通过 http://localhost:3000 访问 public 下的页面资源;页面里的 Ajax 也请求同一端口下的接口,即同源访问。转写稿中调试时先访问接口确认 3000 服务正常,再用 Live Server 的 5500 打开前端复现跨域报错,最后用 CORS 解决;上线方案则改为同源。
易错点:express.static 暴露的是文件夹,访问时要拼资源路径和文件名;转写稿强调路径用绝对路径更严谨,path.join(__dirname, 'public') 中的 __dirname 不要写错;同源访问主要适用于上线部署,本地开发仍可能使用 CORS。
本节的任务:完成今日作业
本节是今日作业(必完成)。具体题目、提交方式与评分要求,请以配套文件夹里 <作业> 目录下的 md 文件为准,这里不重复抄写。
作业会用到的知识就是下面四块:浏览器同源策略、跨域问题、CORS 跨域资源共享、同源访问。下面按“为什么需要 → 怎么想 → 怎么做 → 易错点”的顺序把这一节串一遍,做完再看作业要求,基本就能直接下手。
为什么浏览器要有同源策略
为什么需要:假设你不小心打开了一个来源不明的钓鱼网站,比如 http://icbcbc.com,它把页面做得和真银行一模一样。你输入金额、收款信息和密码点提交,这个页面里的 Ajax 就会把你填的真实信息请求到真正的银行服务器。如果浏览器不管,你的钱就被转给攻击者了。
怎么想:MDN 的定义是——同源策略限制一个源的文档,或它加载的 JS 脚本,能否与另一个源的资源进行交互。它的作用就是阻挡恶意文档、减少可能被攻击的面。
怎么做:不需要你写代码开启,它是浏览器内置的行为,你只要理解它限制了什么:Ajax 只能向同源的 URL 发起请求。
易错点:同源策略是浏览器端的安全机制,不是服务器端的;它限制的是“读响应”,不是“请求发不出去”。
怎么判断“同源”:协议 + 域名 + 端口
为什么需要:要判断一次请求会不会被拦,先得会判断两个 URL 是不是同源。
怎么想:源(origin)由 URL 中的三部分组成——协议、域名、端口号。三者全部一致才是同源,有一个不同就是跨域。
怎么做:拿钓鱼网站的例子对比:
- 网页所在源:
http://icbcbc.com,协议 http、域名icbcbc.com、http 默认端口 80 - Ajax 请求源:
http://icbc.com.cn,协议 http、域名icbc.com.cn、端口 80
协议和端口相同,但域名不同,所以不是同源,浏览器会限制这次 Ajax 的请求响应。
易错点:判断同源不看资源路径,因为源里只有协议、域名、端口三样东西,/api/province 这类路径不参与判断。
跨域问题是怎么产生的
为什么需要:知道同源规则后,就要能看懂实际开发中那条报错。
怎么想:网页加载时所在的源,和网页里内嵌的 Ajax 要请求的源,协议、域名、端口只要有一个不同,就是跨域访问,这次请求和响应会失败。
怎么做:用 Live Server 插件打开前端页面,它启动的服务是 http://localhost:5500;页面里用 axios 请求 http://localhost:3000/api/province。两个源端口不同(5500 与 3000),于是 F12 控制台报出 Access to XMLHttpRequest at ... has been blocked by CORS policy 这类错误,意思就是 XHR 连接出了问题。
易错点:看到这条报错先比三个要素,别急着改前端代码;跨域不是“请求没发出去”,而是响应被浏览器拦下了。
解决方案一:CORS 跨域资源共享(开发阶段用)
为什么需要:现在很多项目是前后端分离的,前端代码和后端代码不在同一个源下,但数据通信还必须正常进行,所以必须解决跨域。
怎么想:CORS 是一种基于 HTTP 头的机制。服务器在响应头里加一个固定字段 Access-Control-Allow-Origin,用来标记“除了跟我同源之外的其他源,也可以访问我服务器上的资源”。前端不需要做额外操作。
怎么做:三步都在后端。
- 在后端项目目录下装包(用包管理器 npm,不是用 node 执行文件):
bash
npm i cors- 在
server.js中引入并注册:
js
const cors = require('cors')
const express = require('express')
const server = express()
// 给当前 web 服务添加插件功能,下面所有接口都开启 CORS
server.use(cors())
server.get('/api/province', (req, res) => {
// 这里返回之前写好的省份列表数据
res.send({ code: 200, msg: '获取省份列表成功', data: provinceList })
})
server.listen(3000, () => {
console.log('http://localhost:3000')
})- 用
nodemon server.js启动服务,浏览器访问http://localhost:3000/api/province,F12 → Network 查看响应头,能看到Access-Control-Allow-Origin: *,说明允许任意源访问。
前端这边照常写就行:
js
axios.get('http://localhost:3000/api/province')
.then(result => {
console.log(result.data)
})
.catch(error => {
console.log(error)
})axios 调用后原地返回的是 Promise 对象,所以能点 .then 拿成功结果、点 .catch 拿失败结果;axios 内部已把 JSON 字符串转成 JS 对象放进 data 里。
易错点:CORS 是后端开启的,前端改代码解决不了;装包命令是 npm i cors;server.use(cors()) 要写在接口之前,才能对下面所有接口生效。
解决方案二:同源访问(上线部署用)
为什么需要:如果上线后还开着 CORS,等于允许任意源(包括攻击者的钓鱼网站)来访问你的接口,很危险。所以开发阶段用 CORS,项目上线面对整个互联网时要关掉它,改用同源访问。
怎么想:让后端这个 web 服务既提供数据接口,也返回前端网页的静态资源。浏览器直接访问 http://localhost:3000/index.html,页面里 Ajax 请求 http://localhost:3000/api/province,两个源完全相同,天然同源,不存在跨域。
怎么做:Express 内置了 static 方法,把前端打包好的文件夹暴露出去。代码里访问其他文件建议写绝对路径,更严谨:
js
const path = require('path')
const express = require('express')
const server = express()
// 暴露 public 文件夹,前端可用 域名+端口+路径+文件名 访问其中资源
server.use(express.static(path.join(__dirname, 'public')))
server.listen(3000)启动后浏览器访问 http://localhost:3000/index.html,就能拿到 public 下的前端页面;页面里 Ajax 请求同源的 /api/province 也能正常拿到数据,而且此时后端并没有开启 CORS。
易错点:express.static 传的是文件夹路径,用 path.join(__dirname, 'public') 拼绝对路径;上线用同源、开发用 CORS,两者不要混着用。
本节小结与作业提示
- 同源策略:限制一个源与另一个源的数据交互,具体限制的是 Ajax 只能访问同源的 URL。
- 同源判断:网页所在源与 Ajax 请求源的协议、域名、端口全部一致,不看路径。
- 跨域:三者有一个不同就产生跨域,控制台会报
Access to XMLHttpRequest ... blocked by CORS policy。 - CORS:后端装
cors包并server.use(cors()),响应头出现Access-Control-Allow-Origin,用于开发阶段的前后端分离项目。 - 同源访问:后端用
express.static同时托管页面与接口,用于上线部署,更安全。
带着这五点去看 <作业> 目录下 md 文件的具体要求,逐条对照完成并提交即可。
本节要解决什么问题
本节从浏览器同源策略讲起,解释什么是跨域、为什么会出现跨域报错,并给出两种处理思路:开发阶段用 CORS 跨域资源共享,上线部署时用同源访问。转写稿里“圆”应统一纠正为“源”,“HX/AJX”指 AJAX,“X优/XU”指 axios,“cars/cos/cse”指 CORS,“le server/li server”指 Live Server 插件。原笔记还列了 package.json、开源许可、npm 命令、export/import 等参考文档,本文保留链接,作为扩展查阅资料。
同源策略:先弄清“源”是什么
为什么需要:浏览器要阻止恶意网页随意读取另一个源的敏感数据。转写稿举例:钓鱼网站伪装成银行页面,如果它能用 AJAX 把用户填写的转账信息发到真正银行服务器并读取结果,就很危险。同源策略限制一个源的文档或脚本与另一个源的资源交互,从而减少攻击面。
怎么想:判断同源只看 URL 中的协议、域名、端口号,三者必须全部一致;资源路径不参与判断。http://localhost:5500 与 http://localhost:3000 端口不同,不同源;http://icbcbc.com 与 http://icbc.com.cn 域名不同,也不同源。
怎么做:遇到 AJAX 请求前,先写出“网页所在源”和“请求目标源”,逐项对比协议、域名、端口。转写稿中 Live Server 打开网页是 http://localhost:5500,请求后端是 http://localhost:3000/api/province,端口不同,所以跨域。
易错点:不要用资源路径判断跨域;不要以为域名相似就是同源;协议从 HTTP 换成 HTTPS、端口从 3000 换成 5500,都可能导致不同源。
跨域问题:报错是怎么出现的
为什么需要:前后端分离项目中,前端页面常在一个源下开发,后端接口在另一个源下运行。浏览器仍执行同源策略,所以 AJAX 请求会失败。
怎么想:先启动后端服务,确认接口本身可用;再用 Live Server 启动前端页面;最后打开 F12 看控制台和 Network。转写稿的调试过程是:在 3000 端口启动后端,浏览器直接访问 http://localhost:3000/api/province 能拿到数据;但从前端 5500 页面里用 axios 请求 3000 接口时,控制台出现 Access to XMLHttpRequest 相关跨域错误。
怎么做:后端启动命令可用:
bash
node server.js
# 或使用 nodemon 监听文件变化
nodemon server.js前端请求核心代码:
js
axios.get('http://localhost:3000/api/province')
.then(res => {
console.log(res.data)
})
.catch(err => {
console.log(err)
})易错点:接口在浏览器地址栏能打开,不代表页面里的 AJAX 一定能跨域读取;跨域失败时先看控制台报错和响应头,不要只改前端请求路径。
CORS:开发阶段让后端允许跨域
为什么需要:前后端分离开发时,前端和后端不在同一个源,但数据通信必须正常进行。CORS 是跨域资源共享机制,基于 HTTP 响应头工作。
怎么想:跨域限制由浏览器执行,解决方案要落到服务器响应上。后端设置固定响应头字段 Access-Control-Allow-Origin,告诉浏览器允许哪些源访问资源。转写稿中使用 cors 本地软件包,把它挂到 express 的 web 服务对象上。
怎么做:在 13 项目后端目录安装依赖:
bash
npm i cors后端核心代码:
js
const cors = require('cors')
// server 是 express 搭建的 web 服务对象
server.use(cors())启动后端后,在 Network 的响应头中可看到 Access-Control-Allow-Origin。前端无需额外操作,仍正常使用 axios 发请求、接收响应。转写稿中刷新 5500 页面后,控制台跨域报错消失,res.data 中拿到省份列表数据。
易错点:CORS 主要在后端配置,不是前端加个请求头就解决;cors() 挂载位置要在接口响应之前;上线后不要长期无条件开放跨域,否则钓鱼网站也可能访问接口。
同源访问:上线部署更安全的做法
为什么需要:转写稿提到,如果上线后后端仍开启 CORS,攻击者页面也可能请求接口。上线部署时更推荐让前端网页和后端接口在同一个 web 服务下,变成同源访问。
怎么想:让后端服务既提供数据接口,也返回前端静态资源。浏览器访问 http://localhost:3000/index.html,页面里的 AJAX 再请求 http://localhost:3000/api/province,协议、域名、端口一致,就是同源访问。
怎么做:把前端页面放入 public 文件夹,后端用 express 的 static 暴露静态资源。转写稿强调路径要写绝对路径更严谨:
js
const path = require('path')
server.use(express.static(path.join(__dirname, 'public')))启动后端后访问 http://localhost:3000/index.html,页面能加载,AJAX 也能拿到数据,且不需要开启 CORS。
易错点:不要手写容易出错的相对路径读取文件;上线同源方案不等于开发阶段也关闭 CORS,开发时仍常配合 CORS;静态资源路径和接口路径要区分清楚。
原笔记参考文档保留
这些文档适合按需查阅:package.json 字段、开源许可协议、npm 命令、ES 模块 import/export 都不要凭记忆写,遇到项目配置或合规问题以官方文档和项目实际为准。
课件图示








代码示例
示例 1:复现跨域问题(前端 Live Server 5500 → 后端 3000)
后端 server.js(端口 3000)
js
// 后端:基于 express 提供省份列表接口
const express = require('express')
const server = express()
server.get('/api/province', (req, res) => {
res.send({
code: 200,
msg: '获取省份列表成功',
data: ['北京市', '上海市', '广东省']
})
})
server.listen(3000, () => {
console.log('Web 服务已启动:http://localhost:3000')
})前端 index.html(用 Live Server 打开,端口 5500)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>跨域演示</title>
<!-- 引入 axios -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<script>
// 目标:请求本机 Web 服务提供的省份列表数据
axios({
url: 'http://localhost:3000/api/province',
// method: 'get' // axios 默认就是 get,可省略
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
</script>
</body>
</html>要点解释 / 易错点
- 判断是否跨域只看 协议、域名、端口 三者,不看资源路径(
/api/province不影响判断)。 - 本例:网页源
http://localhost:5500,请求源http://localhost:3000→ 端口不同 → 跨域。 - 控制台会报
Access to XMLHttpRequest at ... from origin ... has been blocked by CORS policy,then拿不到数据,走到catch。 - 易错点:浏览器地址栏直接访问
http://localhost:3000/api/province是能拿到数据的(不受同源策略限制),只有网页里的 AJAX 才会被拦截,不要误以为后端接口写错了。 - 易错点:用
file://直接双击打开 html 也会跨域,请用 Live Server 起 5500 端口。
示例 2:解决方案 1 —— CORS(开发阶段)
js
// 1. 先下载 cors 本地软件包:npm i cors
const express = require('express')
// 2. 导入 cors 函数
const cors = require('cors')
const server = express()
// 3. 使用 server.use() 给 Web 服务添加插件功能
server.use(cors())
server.get('/api/province', (req, res) => {
res.send({
code: 200,
msg: '获取省份列表成功',
data: ['北京市', '上海市', '广东省']
})
})
server.listen(3000, () => {
console.log('Web 服务已启动:http://localhost:3000')
})前端代码完全不用改,仍是示例 1 的 axios({ url: 'http://localhost:3000/api/province' })。
要点解释 / 易错点
- CORS = 跨域资源共享,基于 HTTP 响应头:
Access-Control-Allow-Origin,允许除自己以外的源访问本服务器资源。 - 关键行
server.use(cors()):server.use()是给整个 Web 服务注册插件,写在所有路由之前,这样下面所有接口的响应都会带上该响应头。 - 验证方法:F12 → Network → 刷新请求 → 查看「响应头」中是否有
Access-Control-Allow-Origin: *。 - 易错点:
npm i cors必须在后端项目文件夹(有package.json的目录)下执行,否则会装到别处导致require('cors')报找不到模块。 - 易错点:CORS 只适用于本地开发阶段;上线后开启
*意味着任何钓鱼网站都能请求你的接口,不安全。
示例 3:解决方案 2 —— 同源访问(上线部署阶段)
后端 server.js(同时提供接口 + 网页资源)
js
const express = require('express')
const path = require('path')
const server = express()
// 暴露指定的文件夹,让前端可以直接拼接路径和资源名字来访问
server.use(express.static(path.join(__dirname, 'public')))
// 数据接口(注意:不再开启 cors)
server.get('/api/province', (req, res) => {
res.send({
code: 200,
msg: '获取省份列表成功',
data: ['北京市', '上海市', '广东省']
})
})
server.listen(3000, () => {
console.log('Web 服务已启动:http://localhost:3000')
})public/index.html(前端网页,随后端一起部署)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>同源访问演示</title>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<script>
// 网页所在源:http://localhost:3000
// 请求的源: http://localhost:3000 → 同源,浏览器放行
axios({
url: 'http://localhost:3000/api/province'
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
</script>
</body>
</html>要点解释 / 易错点
- 做法:让后端 Web 服务既提供数据接口,又返回网页静态资源,前端和后端部署到同一个源下。
- 关键行
server.use(express.static(path.join(__dirname, 'public'))):把public文件夹作为静态资源目录暴露出去,浏览器用http://localhost:3000/index.html即可访问该网页。 - 用绝对路径
path.join(__dirname, 'public')更严谨(__dirname是当前文件所在文件夹的绝对路径)。 - 验证:此时没有
Access-Control-Allow-Origin响应头,控制台也不报错,数据照样拿到 —— 因为网页源与请求源完全一致。 - 易错点:
express.static挂载后访问路径是/index.html,不带public前缀,写成/public/index.html会 404。 - 易错点:前端页面里的请求地址必须与网页所在源一致(同源),如果还写着
5500或别的端口,照样跨域。 - 易错点:上线时记得关闭 CORS,只保留同源访问,接口才不会被非同源网站调用。
常见错误
现象:控制台报
Access to XMLHttpRequest at 'http://localhost:3000/api/province' from origin 'http://localhost:5500' has been blocked by CORS policy,但反复检查 axios 的 url、method、参数都“没错”。 原因:把跨域当成前端代码 bug,在请求头、参数、axios 配置上反复改。 修法:跨域只看协议、域名、端口三者是否一致,与请求参数无关;改前端配置无效,必须去后端处理。现象:以为
/api/province和/api/city属于不同源,或以为把请求改成相对路径就能绕过。 原因:把资源路径、查询串算进了同源判断。 修法:同源只比对协议、域名、端口,路径、参数、hash 一律不参与判断。现象:
npm i cors提示成功、代码里也写了require('cors'),浏览器仍报跨域;或报Cannot find module 'cors'。 原因:终端不在package.json所在目录就安装了,包被装到了别处;或只 require 了却没挂载到服务上。 修法:切到项目根目录安装,确认node_modules/cors存在,且代码里有server.use(cors())这一句。现象:加了 cors 后,一部分接口正常,另一部分仍报跨域。 原因:
server.use(cors())写在了部分路由注册之后,中间件只对其后注册的路由生效。 修法:把server.use(cors())提到所有路由之前,紧跟 server 对象创建之后。现象:后端代码明明改了,刷新浏览器还是同一条报错。 原因:Node 进程没重启,跑的还是旧代码(Node 不会热更新)。 修法:终端 Ctrl+C 停掉,重新
node server.js,再刷新页面。现象:双击 html 用
file://打开,请求被拦,报 origin 为null;后端明明开了 cors 也不通。 原因:file://没有 http 源,不属于同源模型,无法参与 CORS 协商。 修法:用 Live Server 之类起一个 http 服务再测,保证页面有真实的协议、域名、端口。现象:项目上线后,任意第三方站点都能调用自己的接口。 原因:生产环境沿用了开发期的
server.use(cors())(等价于Access-Control-Allow-Origin: *)。 修法:上线时关掉 cors,改为同源部署——server.use(express.static(path.join(__dirname, 'public'))),让页面和接口同一个源。
练习
同源判断:以下每组是否同源?只写判断依据(协议 / 域名 / 端口)。
- a) 页面
http://localhost:5500/index.html请求http://localhost:5500/api/province - b) 页面
http://localhost:5500请求http://localhost:3000/api/province - c) 页面
https://api.shop.com请求http://api.shop.com/api - d) 页面
http://shop.com请求http://shop.com:80/api - e) 页面
http://shop.com请求http://www.shop.com/api
- a) 页面
排错:下面后端代码启动后,前端 5500 端口访问仍报跨域,找出两处问题并改正。
jsconst express = require('express') const server = express() server.get('/api/province', (req, res) => { res.send(['北京', '上海']) }) const cors = require('cors') server.use(cors()) server.listen(3000, () => console.log('running'))(提示:一处与中间件顺序有关,一处与运行状态有关。)
动手:给一个已写好的 express 服务开启跨域资源共享,并写出验证步骤:打开 F12 后要在哪个面板、看哪个响应头字段,才算开启成功。
场景设计:项目要上线,要求接口不被非同源站点调用。请写出让同一个 web 服务既返回网页又提供接口的关键代码,并说明此时前端页面里的请求地址应该怎么写最稳妥。
对比说明:分别说出开发阶段和生产阶段采用的跨域方案,并解释“为什么不能把开发阶段那套直接带上线”。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 浏览器的同源策略 | 05:28 |
| 2 | 跨域问题 | 11:08 |
| 3 | 跨域问题-解决方案1-CORS | 12:26 |
| 4 | 跨域问题-解决方案2-同源访问 | 08:20 |
小结
- 同源判断只有三个维度——协议、域名、端口,路径不参与;三者全同才算同源。
- 跨域报错是浏览器的拦截行为,请求可能已经发出、服务端也可能已响应,所以它不是“前端写错了”。
- CORS 的决定权在服务端响应头,前端唯一要做的就是正常发请求,不要试图用前端手段“绕过”。
- cors 中间件必须挂在所有路由之前,否则会出现“部分接口通、部分接口不通”的诡异现象。
- Node 改完代码必须重启进程,否则一切验证结论都不可信。
- 开发阶段用 CORS,生产阶段用同源部署(后端同时提供接口和静态页面),两套方案各管一段,不能混用。
- 生产环境保留
Access-Control-Allow-Origin: *等于把接口开放给全网,是安全问题而不是配置问题。 - 同源部署后,前端请求写相对路径(如
/api/province)即可天然同源,不要把域名和端口写死。