1 浏览器的同源策略
- 浏览器为确保资源安全,而遵循的一种策略。
2 什么是源
- 源 = 协议 + 域名 + 端口号
- 同源与非同源
-
源1 源2 是否同源 http://www.xyz.com/home htts://www.xyz.com/home 非同源 http://www.xyz.com/home http://mail.xyz.com/home 非同源 http://www.xyz.com:8080/home http://www.xyz.com:8090/home 非同源 http://www.xyz.com:8080/home http://www.xyz.com:8080/search 同源
-
- 所处源与目标源不一致,就是非同源,又称跨域。
3 浏览器对跨域访问的限制
- 如果源A和源B,是非同源的,浏览器会有如下限制
- DOM访问限制: 源A的脚本不能读取和操作源B的DOM
- Cookue访问限制:源A不能访问源B的cookie
- Ajax响应数据限制:源A可以给源B发请求,但是无法获取源B响应的数据
- 浏览器对Aiax获取数据的限制是影响最大的一个,且实际开发中经常遇到
- 跨域限制仅存在浏览器端,服务端不存在跨域限制
4 Ajax响应数据限制演示
-
示意图
-
这里借助nginx演示下
- 修改index.html内容如下
-
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>跨域访问</title> </head> <body> <button onclick="getNews()">获取新闻</button> <script type="text/javascript"> async function getNews(){ const myurl = "https://www.toutiao.com/hot-event/hot-board/?origin=toutiao_pc" let result = await fetch(myurl) let data = await result.json() console.log(data) } </script> </body> </html>
-
- 修改index.html内容如下
-
在谷歌浏览器中访问,可以看到报错了
-
我们所处的源为 http://192.168.206.146,访问的目标源 https://www.toutiao.com,为跨域访问,因此无法获取响应数据。
5 CORS解决Ajax跨域问题
CORS简介
- CORS全称:Cross-Origin Resource Sharing(跨域资源共享),是用于控制浏览器校验跨域请求的一套规范,服务器依照CORS规范,添加特定响应头来控制浏览器校验,规则如下
- 服务器明确表示拒绝跨域请求,或没有表示,则浏览器校验不通过
- 服务器明确表示允许跨域请求,则浏览器校验通过
简单请求与复杂请求
- CORS会把请求分为两类:简单请求和复杂请求。
- 简单请求
- 请求方法为GET、POST、HEAD
- 请求头字段符合《CORS安全规范》。只要不手动修改请求头,一般都能符合该规范
- 请求头的 Content-Type只能是以下三种
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded
- 复杂请求
- 不符合简单请求的要求,就都是复杂请求
- 复杂请求会自动发送预检请求
- 预检请求
- 发送时机:预检请求在实际跨域请求之前发出,由浏览器发起。
- 主要作用:用于向服务器确认是否允许接下来的跨域请求。
- 基本流程:先发起OPTIONS请求,如果通过预检,继续发起实际的跨域请求。
- 请求头内容:一个OPTIONS预检请求,通常会包含如下请求头
- Origin: 发起请求的源
- Access-Control-Request-Method: 实际请求的HTTP方法
- Access-Control-Request-Headers: 实际请求中使用的自定义头
CORS解决简单请求的跨域问题
- 示意图
- 浏览器请求时,请求头都会带一个Origin,表示源地址。可以在服务端的响应头中加一个 Access-Control-Allow-Origin字段,表示允许哪个源访问,填写允许访问的源地址,就可以解决跨域问题。
CORS解决复杂请求的跨域问题
- 第一步:服务器先通过浏览器的预检请求,返回如下响应头
- Access-Control-Allow-Origin: http://192.168.206.146:80
- Access-Control-Allow-Methods: GET
- Access-Control-Allow-Headers: myname
- Access-Control-Max-Age: 7200
- 备注: Access-Control-Allow-Headers是自定义的响应头,Access-Control-Max-Age是可选的,缓存时间,表示多长时间内不需要再发送预检请求。
- 示意图
- 第二步:处理实际的跨域请求(和处理简单请求的跨域问题方法一样)
通过cors解决跨域问题
- 上面演示的方法,手动修改比较麻烦,可以通过cors去配置。
- 安装cors后,在服务端的js代码中写入以下内容
-
const cors = require('cors') app.use(cors({ origin: 'http://192.168.206.146:80', // 允许的源 methods: ['GET', 'POST','OPTIONS'], // 允许的方法 allowedHeaders: ['myname'] //允许的自定义头 }))
6 JSONP解决Ajax跨域问题
JSONP概述
- JSONP概述:JSONP是利用了 <script> 标签可以跨域加载脚本,且不受严格限制的特性去解决跨域问题。但JSONP有局限性,只能解决GET请求的跨域问题。
原理
- 基本流程
- 第一步:客户端创建一个 <script> 标签,并将其src属性设置为包含跨域请求的URL,同时准备一个回调函数,这个回调函数用于处理返回的数据。
- 第二步:服务端接收到请求后,将数据封装在回调函数中并返回。
- 第三步:客户端的回调函数被调用,数据以参数的形式传入回调函数。
- 示意图
代码实现
- server端代码
-
const express = require('express') const app = express() const teachers = [ {id:'001', name:'jack', age:18}, {id:'002', name:'lucy', age:20}, ] app.get('/teachers', (req, res)=>{ res.send(`callback(${JSON.stringify(teachers)})`) }) app.listen(80, ()=>{ console.log('server listen') })
-
- 前端代码
-
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>跨域访问</title> </head> <body> <script type="text/javascript"> function callback(data){ console.log('callback 被调用', data) } </script> <script type="text/javascript" src="http://127.0.0.1:80/teachers"></script> </body> </html>
-
- 前端代码优化-设置点击按钮再发送请求
-
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>跨域访问</title> </head> <body> <button onclick="getData">获取数据</button> <script type="text/javascript"> function callback(data){ console.log(data) } function getData(){ const script = document.createElement('script') script.onload = ()=>{ script.remove() } script.src = 'http://127.0.0.1:80/teachers' document.body.appendChild(script) } </script> </body> </html>
-
7 配置代理解决跨域
-
这里只介绍通过Nginx反向代理解决跨域。
-
在nginx配置文件中配置反向代理
-
server { listen 80; server_name localhost; location / { root html; index index.html index.htm; } # 配置反向代理 location /api { proxy_pass https://www.toutiao.com/hot-event/hot-board/?origin=toutiao_pc; add_header Access-Control-Allow-Origin *; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } }
-
-
修改Nginx默认index.html页面如下
-
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>跨域访问</title> </head> <body> <button onclick="getNews()">获取新闻</button> <script type="text/javascript"> async function getNews(){ const myurl = "http://192.168.206.146:80/api" let result = await fetch(myurl) let data = await result.json() console.log(data) } </script> </body> </html>
-
-
在标题4的演示中,直接在浏览器访问 http://192.168.206.146:80,点击获取新闻,会访问对应链接 https://www.toutiao.com,由于存在跨域,因此访问失败。
-
配置反向单例后,在浏览器访问 http://192.168.206.146:80, 点击获取新闻,会访问 http://192.168.206.146:80/api 这个地址,而 http://192.168.206.146:80/api 实际代理到了 https://www.toutiao.com 地址上。由于服务器之间不存在跨域问题,因此可以正常访问。
-
效果
-
点击获取新闻,可以看到,可以成功获取到数据。







8561

被折叠的 条评论
为什么被折叠?



