最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Node系列之解决同源策略跨域问题的9种方案(最新最全)

    正文概述 掘金(前端开发小马哥)   2020-12-03   473

    跨域

    什么是跨域

    跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。

    广义的跨域:

    1.资源跳转: A链接、重定向、表单提交
    2.资源嵌入: <link>、<script>、<img>、<frame>等dom标签,还有样式中background:url()、@font-face()等文件外链
    3.脚本请求: js发起的ajax请求、dom和js对象的跨域操作等
    

    其实我们通常所说的跨域是狭义的,是由浏览器同源策略限制的一类请求场景。

    什么是同源策略?

    同源策略/SOP(Same origin policy)是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。

    同源策略限制以下几种行为:

    1.) Cookie、LocalStorage 和 IndexDB 无法读取
    2.) DOM 和 Js对象无法获得
    3.) AJAX 请求不能发送
    

    常见跨域场景

    URL                                      说明                    是否允许通信
    http://www.domain.com/a.js
    http://www.domain.com/b.js         同一域名,不同文件或路径           允许
    http://www.domain.com/lab/c.js
    
    http://www.domain.com:8000/a.js
    http://www.domain.com/b.js         同一域名,不同端口                不允许
     
    http://www.domain.com/a.js
    https://www.domain.com/b.js        同一域名,不同协议                不允许
     
    http://www.domain.com/a.js
    http://192.168.4.12/b.js           域名和域名对应相同ip              不允许
     
    http://www.domain.com/a.js
    http://x.domain.com/b.js           主域相同,子域不同                不允许
    http://domain.com/c.js
     
    http://www.domain1.com/a.js
    http://www.domain2.com/b.js        不同域名                         不允许
    

    演示跨域

    server.js

    const http = require('http');
    const fs = require('fs');
    http.createServer((req, res) => {
        const { url, method } = req;
        if (url === '/' && method === 'GET') {
            // 读取首页
            fs.readFile('./index.html', (err, data) => {
                if (err) {
                    res.statusCode = 500;//服务器内部错误
                    res.end('500- Interval Serval Error!');
                }
                res.statusCode = 200;//设置状态码
                res.setHeader('Content-Type', 'text/html');
                res.end(data);
            })
        } else if (url === '/user' && method === 'GET') {
            res.statusCode = 200;//设置状态码
            res.setHeader('Content-Type', 'application/json');
            res.end(JSON.stringify([{ name: "小马哥" }]));
        }
    
    }).listen(3000);
    

    axios发起请求

    axios.get('http://127.0.0.1:3000/user').then(res => {
        console.log(res.data);
    
    }).catch(err => {
        console.log(err);
    
    })
    

    Node系列之解决同源策略跨域问题的9种方案(最新最全)

    跨域常用解决方案

    1. 通过jsonp跨域
    2. 跨域资源共享(CORS 最常用)
    3. nginx代理跨域
    4. nodejs中间件代理跨域

    通过JSONP跨域

    通常为了减轻web服务器的负载,我们把js、css,img等静态资源分离到另一台独立域名的服务器上,在html页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许,基于此原理,我们可以通过动态创建script,再请求一个带参网址实现跨域通信

    axios最新版本已经不支持jsonp方法了,不想因为一个jsonp请求就又去引一个依赖,所以决定自己封装一下

    axios.jsonp = (url) => {
        if (!url) {
            console.error('Axios.JSONP 至少需要一个url参数!')
            return;
        }
        return new Promise((resolve, reject) => {
            window.jsonCallBack = (result) => {
                resolve(result)
            }
            var JSONP = document.createElement("script");
            JSONP.type = "text/javascript";
            JSONP.src = `${url}callback=jsonCallBack`;
            document.getElementsByTagName("head")[0].appendChild(JSONP);
            setTimeout(() => {
                document.getElementsByTagName("head")[0].removeChild(JSONP)
            }, 500)
        })
    }
    
    // 第一种 通过jsonp
    axios.jsonp('http://127.0.0.1:3000/user?')
        .then(res=>{
        console.log(res);
    }).catch(err=>{
        console.log(err);
    
    })
    

    server.js

    const express = require('express');
    const fs = require('fs');
    const app = express();
    // 中间件方法
    // 设置node_modules为静态资源目录
    // 将来在模板中如果使用了src属性 http://localhost:3000/node_modules
    app.use(express.static('node_modules'))
    app.get('/',(req,res)=>{
      fs.readFile('./index.html',(err,data)=>{
        if(err){
          res.statusCode = 500;
          res.end('500 Interval Serval Error!');
        }
        res.statusCode = 200;
        res.setHeader('Content-Type','text/html');
        res.end(data);
      })
    })
    // app.set('jsonp callback name', 'cb')
    app.get('/api/user',(req,res)=>{
      console.log(req);
      // http://127.0.0.1:3000/user?cb=jsonCallBack
      // const cb = req.query.cb;
      // cb({})
      // res.end(`${cb}(${JSON.stringify({name:"小马哥"})})`)
      res.jsonp({name:'小马哥'})
    })
    app.listen(3000);
    

    jsonp缺点:只能实现get一种请求。

    Nodejs中间件代理跨域

    实现原理:同源策略是浏览器需要遵循的标准,而如果是服务器向服务器请求就无需遵循同源策略。 代理服务器,需要做以下几个步骤:

    • 接受客户端请求 。
    • 将请求 转发给服务器。
    • 拿到服务器 响应 数据。
    • 将 响应 转发给客户端。

    Node系列之解决同源策略跨域问题的9种方案(最新最全)

    index.html文件,通过代理服务器http://127.0.0.1:8080向目标服务器http://localhost:3000/user请求数据

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>Document</title>
    </head>
    
    <body>
      <script src='/axios/dist/axios.js'></script>
      <h2>中间件代理跨域</h2>
      <script>
        axios.defaults.baseURL = 'http://localhost:8080';    
        axios.get('/user')
          .then(res => {
            console.log(res);
    
          })
          .catch(err => {
            console.log(err);
    
          }) 
      </script>
    
    </body>
    
    </html>
    

    proxyServer.js代理服务器

    const express = require('express');
    const { createProxyMiddleware } = require('http-proxy-middleware');
    const app = express();
    
    // 代理服务器操作
    //设置允许跨域访问该服务.
    app.all('*', function (req, res, next) {
      res.header('Access-Control-Allow-Origin', '*');
      res.header('Access-Control-Allow-Headers', 'Content-Type');
      res.header('Access-Control-Allow-Methods', '*');
      res.header('Content-Type', 'application/json;charset=utf-8');
      next();
    });
    
    // http-proxy-middleware
    // 中间件 每个请求来之后 都会转发到 http://localhost:3001 后端服务器
    app.use('/', createProxyMiddleware({ target: 'http://localhost:3001', changeOrigin: true }));
    
    app.listen(8080);
    

    业务服务器server.js

    const express = require('express');
    const fs = require('fs');
    const app = express();
    // 中间件方法
    // 设置node_modules为静态资源目录
    // 将来在模板中如果使用了src属性 http://localhost:3000/node_modules
    app.use(express.static('node_modules'))
    app.get('/',(req,res)=>{
      fs.readFile('./index.html',(err,data)=>{
        if(err){
          res.statusCode = 500;
          res.end('500 Interval Serval Error!');
        }
        res.statusCode = 200;
        res.setHeader('Content-Type','text/html');
        res.end(data);
      })
    })
    // app.set('jsonp callback name', 'cb')
    
    app.get('/user',(req,res)=>{
      res.json({name:'小马哥'})
    })
    app.listen(3001);
    

    通过CORS跨域

    //设置允许跨域访问该服务.
    app.all('*', function (req, res, next) {
        /// 允许跨域访问的域名:若有端口需写全(协议+域名+端口),若没有端口末尾不用加'/'
        res.header('Access-Control-Allow-Origin', '*');
        res.header('Access-Control-Allow-Headers', 'Content-Type');
        res.header('Access-Control-Allow-Methods', '*');
        res.header('Content-Type', 'application/json;charset=utf-8');
        next();
    });
    

    具体实现:

    • 响应简单请求:动词为get/post/head,如果没有自定义请求头,Content-Typeapplication/x-www-form-urlencoded,multipar/form-datatext/plagin之一,通过添加以下解决

       res.header('Access-Control-Allow-Origin', '*');
      
    • 响应prefight请求,需要响应浏览器发出的options请求(预检请求),并根据情况设置响应头

      //设置允许跨域访问该服务.
      app.all('*', function (req, res, next) {
          res.header('Access-Control-Allow-Origin', 'http://localhost:3002');
          //允许令牌通过
          res.header('Access-Control-Allow-Headers', 'Content-Type,X-Token');
          res.header('Access-Control-Allow-Methods', 'GET,POST,PUT');
          //允许携带cookie
          res.header('Access-Control-Allow-Credentials', 'true');
          res.header('Content-Type', 'application/json;charset=utf-8');
          next();
      });
      

      前端

      在这里给大家补充了axios相关用法,具体的看视频

      <!DOCTYPE html>
      <html lang="en">
        
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
      </head>
        
      <body>
        <script src='/axios/dist/axios.js'></script>
        <script src="https://cdn.bootcss.com/qs/6.9.1/qs.js"></script>
        <h2>CORS跨域</h2>
        <script>
          axios.defaults.baseURL = 'http://127.0.0.1:3002';
          axios.defaults.headers.common['Authorization'] = 'xaxadadadadad';
          axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
        
          axios.interceptors.request.use(function (config) {
            let data = config.data;
            data = Qs.stringify(data);
            config.data = data;
            // 在发送请求之前做些什么
            return config;
          }, function (error) {
            // 对请求错误做些什么
            return Promise.reject(error);
          });
          axios.post('/login', {
              username: 'xiaomage',
              password: 123
            }, {
              // headers: {
              //   'Authorization': 'adjahdj1313131'
              // },
              // 表示跨域请求时需要使用凭证 允许携带cookies
              withCredentials: true
            })
            .then(res => {
              console.log(res);
            }).catch(err => {
              console.log(err);
        
            })
        </script>
        
      </body>
        
      </html>
      

    使用第三方插件cors

    npm i cors -S
    

    server.js

    const cors = require('cors');
    //简单使用
    app.use(cors())
    

    配置选项参考链接

    ex:

    app.use(cors({
      origin:'http://localhost:3002', //设置原始地址
      credentials:true, //允许携带cookie
      methods:['GET','POST'], //跨域允许的请求方式
      allowedHeaders:'Content-Type,Authorization' //允许请求头的携带信息
    }))
    

    nginx反向代理

    实现原理类似于Node中间件代理,需要你搭建一个中转nginx服务器,用于转发请求。

    使用nginx反向代理实现跨域,是最简单的跨域方式。只需要修改nginx的配置即可解决跨域问题,支持所有浏览器,支持session,不需要修改任何代码,并且不会影响服务器性能。

    实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。

    // proxy服务器
    server {
        listen       81;
        server_name  www.domain1.com;
        location / {
            proxy_pass   http://www.domain2.com:8080;  #反向代理
            proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
            index  index.html index.htm;
            # 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
            add_header Access-Control-Allow-Origin http://www.domain1.com;  #当前端只跨域不带cookie时,可为*
            add_header Access-Control-Allow-Credentials true;
        }
    }
    

    起源地下载网 » Node系列之解决同源策略跨域问题的9种方案(最新最全)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元