今天面试官小姐姐问到如何解决跨域问题,我答:用JSONP、用CROS。她继续追问在vue中如何解决跨域呢?我:...
跨域
跨域本质是浏览器基于同源策略的一种安全手段。
同源策略是一种约定,是浏览器最核心和最基本的安全功能。不同源的页面之间,不准互相访问数据。若 JS 运行在源A 里,那么只能获取A 的数据,不能获取源B 的数据,即不允许跨域。
<!-- 通过 window.origin 或 location.origin得到 '源' -->
https://www.baidu.com
源就是一个标识。由协议、域名(主机)、端口号三部分组成。
同源就是以上三部分都相同,反之非同源请求,即以上三项其中一项不相同的时候,就会产生跨域。
同源策略
同源策略限制的是数据访问,以用户登录QQ空间获取好友列表为例:
- hacker 获取好友列表,用 AJAX 请求
/friends.json - 正常获取好友列表:用 AJAX 请求
/friends.json+Referer字段(HTTP请求头)
- 跨域提示:
如何解决
解决跨域的方法有三种:JSONP 、 CORS 、 Proxy
JSONP
适用于兼容 IE 的,因为 IE 不支持跨域。
在跨域时,当前浏览器不支持CROS,就使用 JSONP 支持跨域。具体实现是请求一个 JS 文件,执行回调,回调中即为数据。
JSONP 的优点是兼容 IE,并支持跨域。缺点是不能像 AJAX 精确获得状态,响应头这些详细信息,只知道成功或失败;只能发 GET 请求,不支持 POST 请求。
// friends.js
window.xxx = {{data}}
if(path === 'friend.js'){
response.statusCode = 200
response.setHeader('Content-Type','text/json;charset=utf-8')
// 把允许跨域访问的域名添加到清单中
const string = fs.readFileSync('./public/friends.js').toString();
const data = fs.readFileSync('./public/friends.json').toString();
const string2 = string.replace("{{data}}",data);
response.write(string2);
response.end()
}
// frank.js
const script = document.createElement('script')
script.src = 'http://qq.com:8888/friend.js'
script.onload = ()=>{
console.log(window.xxx)
}
document.body.appendChild(script)
在vue项目中,主要针对 CORS 或 Proxy 这两种方案展开。
CORS
CORS 是一个系统,跨域资源共享,由一系列传输的 HTTP 头组成,这些 HTTP 头决定浏览器是否阻止前端 JS 代码获取跨域请求的响应。如果要共享数据,就需要提前声明。
后端实现 CORS 就是增加一些 HTTP 头,让服务器能声明允许的访问来源
以koa框架举例:添加中间件,直接设置Access-Control-Allow-Origin响应头
app.use(async (ctx, next)=> {
ctx.set('Access-Control-Allow-Origin', 'host');
ctx.set('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
ctx.set('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
if (ctx.method == 'OPTIONS') {
ctx.body = 200;
} else {
await next();
}
})
Node.js服务器中可以这样设置:
if(path === 'friend.json'){
response.statusCode = 200
response.setHeader('Content-Type','text/json;charset=utf-8')
// 把允许跨域访问的域名添加到清单中
response.setHeader('Access-Control-Allow-Origin', 'http://frank.com:9990')
response.write(fs.readFileSync('./public/friends.json'))
response.end()
}
Proxy
网络代理,一种特殊的网络服务,允许客户端通过这个服务与服务器进行非直接的连接。
方案一:前端实现
对于通过vue-cli脚手架搭建的项目,通过webpack为本地服务器作为请求的代理对象,通过该服务器转发请求至目标服务器,得到结果再转发给前端,在vue.config.js文件中配置:
amodule.exports = {
devServer: {
host: '127.0.0.1',
port: 8084,
// vue项目启动时自动打开浏览器
open: true,
proxy: {
// '/api'是代理标识,用于告诉node,url前面是/api的就是使用代理的
'/api': {
//目标地址,一般是指后台服务器地址
target: "http://xxx.xxx.xx.xx:8080",
//是否跨域
changeOrigin: true,
// pathRewrite 的作用是把实际 Request Url中的'/api'用 "" 代替
pathRewrite: {
'^/api': ""
}
}
}
}
}
通过 axios发送请求中,配置请求的根路径
axios.defaults.baseURL = '/api'
方案二:服务器实现代理请求转发
以express框架为例
var express = require('express');
const proxy = require('http-proxy-middleware')
const app = express()
app.use(express.static(__dirname + '/'))
app.use('/api', proxy({ target: 'http://localhost:4000', changeOrigin: false}));
module.exports = app
方案三:配置ngix实现代理
server {
listen 80;
# server_name www.josephxia.com;
location / {
root /var/www/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://127.0.0.1:3000;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}