We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
所谓的跨域是指:只要协议 http/https,域名,端口号有一个不一样就是跨域请求(非同源请求)。例如:http://example.com 和 http://api.example.com 之间的请求就是跨域请求。真实项目当中跨域是很常见的,真正的同源项目很少。
http/https
http://example.com 和 http://api.example.com
同源策略是浏览器的一个安全策略,如果两个 URL 协议、域名端口号一致就是同源,不一致就是非同源
跨域是由于服务器的安全策略限制导致的,后端不存在跨域的问题。
http://localhost:3000
跨域有助于分离开发和部署。
渲染进程、浏览器主进程、GPU 进程、插件进程、网络进程
渲染进程
xhr.send()
沙箱
消息队列、共享内存、管道、套接字 socket、信号量
Unix socket
所谓的沙箱是 操作系统限制进程对内存地址的一个访问权限。
浏览器的安全策略会对 ajax 和 fetch 的跨域请求会有限制。提示一个跨域请求的错误信息。
JSONP 实现跨域的原理:利用 script(/link/img也可以) 标签不存在跨域的限制,请求数据信息。
// 本地的地址是 http://localhost:3000 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
本地的代码请求了一个域名和协议都不相同的地址,就会造成跨域,但是利用浏览器不对 script 标签请求限制实现跨域请求。
一开始客户端向服务器发送了一个带有回调函数的url请求链接,比如
<script src="http://168.0.0.1/api/list?callback=func">
服务端接收到这个 callback 函数后执行,准备客服端需要的数据传递给客服端。浏览器接收到数据后执行服务器返回的回调函数。这个回调函数是一个全局下的函数。
callback
?
http://168.0.0.1/api/list?callback=func
// 客户端 index.js function jsonp(url, callback) { let script; url = url.includes('?') ? `${url}&${callback}` : `${url}?${callback}` script = document.createElement('script') script.src = url document.body.appendChild(script) // window['callback'] = callback // 这里加一层判断,callback 是否有放回值 window['callback'] = (data) =>{ callback && callback(data) delete window['callback'] } } jsonp('http://127.0.0.1:3000', (data) => { console.log(data) }) // server.js let express = require('express') let app = express() app.get('/', function(req, res) { let {callback} = req.query console.log(callback) let data = { a: 12 } res.send(`callback(${JSON.stringify(data)})`) }) app.listen(3000, () => { console.log('ok') })
特别注意,浏览器获取到 callback(${JSON.stringify(data)}) 后发现这段代码是字符串函数,会解析字符串函数,在全局环境下执行这段代码,所以我们需要在全局环境 window 中先挂载这个函数。
callback(${JSON.stringify(data)})
window
JSONP 的缺点也很明显,JSONP 利用的是 script 标签实现的跨域请求,但是 script 标签只能发送 GET 请求,不能发送 POST 等请求。
CORS 需要浏览器和服务器端的配合,主要实现是在服务端设置允许的请求的第三方源。让浏览器跨域的安全策略可以允许第三方源的请求。
GET、POST 或者 HEAD 发起的请求
origin:域名
Access-Control-Allow-Origin:域名
origin
PUT、delete、connection等
预检请求
option
OPTIONS / HTTP/1.1 Origin: 当前地址 Host: xxx.com Access-Control-Request-Method: PUT Access-Control-Request-Headers: X-Custom-Header
Access-Control-Allow-Origin
HTTP/1.1 200 OK Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, POST, PUT Access-Control-Allow-Headers: X-Custom-Header Access-Control-Allow-Credentials: true Access-Control-Max-Age: 1728000 Content-Type: text/html; charset=utf-8 Content-Encoding: gzip Content-Length: 0
// 设置可以跨域的请求源, Access-Control-Allow-Origin: "http://loacalhost:3000" // 设置是否可以携带资源凭证 cookie Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin 可以设置所有源 *,也可以携带单一源。注意所有源不能携带资源凭证 cookie,单一源可以携带。
*
Proxy 跨域代理是在 webpack 中 devServer 配置需要跨域的域名
devServer
const path = require('path') module.exports = { entry:'./index.js', output:path.resolve(__dirname, 'dist'), devServer:{ port: '3000', hot: true, proxy:{ '/api': { target: 'http://168.0.1.1:1001', changeOrigin: true, }, } } }
proxy 表示访问 /api 的接口都会被代理到 http://localhost:3000 上面。
/api
上面我们提到过跨域是浏览器的安全策略限制导致的,但是后台之间的通信不存在跨域的问题。webpack 中的 devServer 会在本地开始一个node服务端口号是 3000,虽然外部服务器的端口号是 1001 也是可以通信的。node 在上面中提供了中间层的代理而已,负责转发和接收的作用。
3000
1001
缺点:这种方式只适合在本地开发,生产环境不适合。那么如何在生产环境下也适合呢?这可以使用 nginx 代理
nginx 代理也是起到一个中间处理的作用,在 nginx 配置中,配置需要代理的域名即可。
// proxy服务器 server { listen 81; server_name www.example.com; location / { proxy_pass http://www.example2.com:8080; #反向代理 root html; } }
if
add_header
Allow-access-control-origin
location ^~ /api/v1 { add_header 'Access-Control-Allow-Origin' "$http_origin"; add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS'; add_header 'Access-Control-Allow-Credentials' 'true'; if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' "$http_origin"; add_header 'Content-Type' 'text/html charset=UTF-8'; add_header 'Content-Length' 0; return 200; } # 这下面是要被代理的后端服务器,它们就不需要修改代码来支持跨域了 proxy_pass http://127.0.0.1:8085; proxy_set_header Host $host; proxy_redirect off; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_connect_timeout 60; proxy_read_timeout 60; proxy_send_timeout 60; }
The text was updated successfully, but these errors were encountered:
No branches or pull requests
跨域
跨域限制的是什么
什么是同源策略
产生跨域的原因
http://localhost:3000
和获取线上服务器的数据也会造成跨域问题。浏览器是怎么实现将数据给劫持的?
浏览器中的进程
渲染进程、浏览器主进程、GPU 进程、插件进程、网络进程
。渲染进程
又可以划分多个线程 GUI渲染线程、异步 http 请求线程、js引擎线程、定时器线程、事件处理线程。劫持原理
xhr.send()
时,浏览器为了防止黑客通过脚本获取到系统资源,浏览器会将渲染进程放入到沙箱
中。在沙箱中欧你的渲染进程是没有办法进行网络请求的。但是只能通过网络进程发送请求,所以这里设计到进程间的通信方式。在操作系统中进程的通信方式有消息队列、共享内存、管道、套接字 socket、信号量
等。Unix socket
套接字,实现进程之间的相互通信,发起网络请求。服务端将响应发送回去,浏览器会去检查响应头中是否存在允许的跨域域名,没有就将响应体丢掉,达到数据劫持的目的。常见的三种跨域方式
JSONP
JSONP 具体实现跨域原因
一开始客户端向服务器发送了一个带有回调函数的url请求链接,比如
服务端接收到这个
callback
函数后执行,准备客服端需要的数据传递给客服端。浏览器接收到数据后执行服务器返回的回调函数。这个回调函数是一个全局下的函数。模拟实现 JSONP 的跨域请求函数
?
携带一个回调函数必须是一个全局函数。例如上面的http://168.0.0.1/api/list?callback=func
。JSONP 缺陷
CORS 跨域
CORS 跨域的简单请求
origin:域名
(要向服务器请求的源),服务器拿到请求的源后,会响应回一个字段Access-Control-Allow-Origin:域名
,如果origin
不在这个字段的范围内,浏览器就会抛出跨域的错误。成功的话才会发送真正的请求。CORS 跨域的非简单请求
预检请求
,预检请求的请求方法是option
,同时也会携带上origin
字段。Access-Control-Allow-Origin
,那么才会发送真正的请求体。CORS 跨域具体实现
Proxy
webpack 的 Proxy
webpack 的 proxy 跨域原理
缺点:这种方式只适合在本地开发,生产环境不适合。那么如何在生产环境下也适合呢?这可以使用 nginx 代理
nginx 代理
nginx 中配置 cors 实现跨域
if
判断请求的域名信息,如果命中就通过add_header
写入允许跨域的请求源Allow-access-control-origin
域名,凭证,请求方法等。总结
The text was updated successfully, but these errors were encountered: