vue项目中解决跨域问题

375 阅读3分钟

今天面试官小姐姐问到如何解决跨域问题,我答:用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请求头)

image.png

  • 跨域提示:

image.png

如何解决

解决跨域的方法有三种: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项目中,主要针对 CORSProxy 这两种方案展开。

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;
    }
}