node代理解决跨域

node实现代理服务器解决跨域

1、产生跨域

1.1、什么是同源策略及其限制内容?
  • 同源策略

    • 同源策略是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSRF等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源

    在这里插入图片描述

  • 同源策略限制内容有

    • Cookie、LocalStorage、IndexedDB 等存储性内容
    • DOM 节点
    • AJAX 请求发送后,结果被浏览器拦截了
  • 跨域场景

    在这里插入图片描述

2、node 实现代理解决跨域

2.1、代理原理
  • 同源策略是浏览器需要遵循的标准,而如果是服务器向服务器请求就无需遵循同源策略

  • 实现步骤

    • 使用node启动一个代理服务器
    • 客户端发送请求到代理服务器
    • 代理服务器去目标服务器请求数据
    • 目标服务器将数据返回给代理服务器
    • 代理服务器将数据返回给客户端
  • 图解

在这里插入图片描述

2.2、CORS跨域资源共享
  • 简介
    • CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
    • 整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
    • 因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
  • 两种请求
    • 简单请求

      • 满足以下条件是简单请求

        • 1)、请求方法是以下三种方法之一:

          • HEAD
          • GET
          • POST
        • 2)、HTTP的头信息不超出以下几种字段:

          • Accept
          • Accept-Language
          • Content-Language
          • Last-Event-ID
          • Content-Type:只限于三个值application/x-www-form-urlencodedmultipart/form-datatext/plain
      • CORS设置响应头

        • 1)、Access-Control-Allow-Origin
        • 2)、[Access-Control-Allow-Credentials]
        • 3)、[Access-Control-Expose-Headers]
    • 非简单请求

      • 字面意思,不是简单请求就是非简单请求
      • CORS设置响应头
        • 1)、Access-Control-Allow-Origin
        • 2)、Access-Control-Allow-Methods
        • 3)、[Access-Control-Allow-Headers]
        • 4)、[Access-Control-Allow-Credentials]
        • 5)、[Access-Control-Max-Age]
  • 响应头介绍
    • 1)、Access-Control-Allow-Origin
      • 该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。
    • 2)、Access-Control-Allow-Credentials
      • 该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。
    • 3)、Access-Control-Expose-Headers
      • 该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('FooBar')可以返回FooBar字段的值。
    • 4)、Access-Control-Allow-Methods
      • 该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。
    • 5)、Access-Control-Allow-Headers
      • 如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。
    • 6)、Access-Control-Max-Age
      • 该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。
  • 更多信息参考阮一峰:https://www.ruanyifeng.com/blog/2016/04/cors.html
2.3、假设一个场景
  • 客户端
    • http://localhost:8000
  • 代理服务器
    • http://localhost:8001
  • 目标服务器
    • http://opendata.baidu.com
  • 场景
    • 客户端想拿到目标服务器的日历信息,但是目标服务器没有设置CORS跨域资源共享,此时我们可以让代理服务器帮我们去目标服务器拿我们想要的数据。
2.4、实现过程
  • 客户端实现

    • index.html

      <!DOCTYPE html>
      <html lang="en">
      <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
      </head>
      <body>
        <button id="button">获取日历信息</button>
      </body>
      </html>
      <script>
        const button = document.getElementById('button')
        button.addEventListener('click',async ()=>{
          fetch('http://localhost:8001/calendar',{method: 'PUT',}).then(res => res.text()).then(res =>{
            console.log(res)
          })
        })
      </script>
      
    • client.js

      // 提供一个http 服务
      const http = require('http');
      // fs 模块  用于读取文件的
      const fs = require('fs')
      // 创建一个服务
      const server = http.createServer((request,response)=>{
        let pathname = request.url  //先获取地址
        pathname=pathname=='/'?'/index.html':pathname //根目录下定位到首页  
        console.log(pathname)
          // 读取目标文件
          fs.readFile('.' + pathname, (err,data)=>{
            if (err) {
              response.writeHead(404, { 'Content-Type': 'text/html;charset="utf-8"' });
              response.end('<h3>404</h3>');
            } else {
              response.writeHead(200, { 'Content-Type': 'text/html;charset="utf-8"' });
              response.end(data);
            }
          } )
      })
      server.listen(8000)
      
    • node client.js 启动服务

在这里插入图片描述

  • 代理服务器-nodejs实现

    • node-proxy.js

      const http = require('http')
      const iconv = require('iconv-lite');
      const port = 8001
      const server = http.createServer((req,res)=>{
        // 简单设置下cors
        res.setHeader("Access-Control-Allow-Origin","*")
        res.setHeader("Access-Control-Allow-Methods","*")
        if (req.url.split('/')[1] === 'calendar') {
          console.log('start calendar')
          const options = {
            hostname: 'opendata.baidu.com',
            path: '/api.php?query=2020%E5%B9%B45%E6%9C%88&resource_id=6018&format=json',
            method: 'GET',
          }
          const calendar = http.request(options)
          calendar.on('response',calendarRes => {
            res.setHeader('Content-Type', 'text/html;charset=utf-8')
            let d = null
            calendarRes.on('data',data=>{
              // node 不支持 gbk编码 借助iconv转译
              // 解码
              const t = iconv.decode(data,'GBK')
              // 编码
              const r = iconv.encode(t,'utf8')
              d +=r
            })
            calendarRes.on('end',()=>{
              res.end(d)
            })
          })
          calendar.on('error',calendarErr => {
            console.log(calendarErr)
          } )
          calendar.end()
        } else {
          res.end('666')
      
        }
      
      })
      
      server.listen(port,()=>{
        console.log(`服务器运行在 localhost:${port}/`)
      })
      
    • 效果

在这里插入图片描述

  • 到这里我们的代理服务器完成了

    • 应对复杂请求,代理多个域名没有支持,这里就不实现了,有兴趣可以自己玩玩
  • 代理服务器-express+http-proxy-middleware 实现

    • 客户端小修改index.html

      button.addEventListener('click',async ()=>{
          // http://localhost:8001/calendar
          fetch('http://localhost:8001/calendar/api.php?query=2020年5月&resource_id=6018&format=json',{method: 'PUT',}).then(res => res.text()).then(res =>{
            console.log(res)
            document.body.innerText = res
          })
        })
      
    • 代理服务器

      const express = require('express')
      const iconv = require('iconv-lite');
      const port = 8001
      const app = express()
      app.get('/', (req, res) => {
        res.send('Hello World!')
      })
      const cors = function(req, res, next){
        console.log('LOGGED')
        // get 方法会自动添加,其他方法需要手动添加
        res.set({
          "Access-Control-Allow-Origin":"*",
          "Access-Control-Allow-Methods":"*",
          // 'Content-Type':"text/html;charset=utf-8"
        })
        next()
      }
      app.use(cors)
      
      // ------------------- 代理获取日历信息
      
      function onProxyReq(proxyReq, req, res) {
        console.log('onProxyReq',req.method)
        proxyReq.method = 'GET'
        console.log('onProxyReq',proxyReq.method)
      }
      function onProxyRes(proxyRes, req, res) {
        console.log('onProxyRes',proxyRes.headers)
        res.set('Content-Type',"text/html;charset=utf-8")
        let d = null
        proxyRes.on('data',data => {
          // node 不支持 gbk编码 借助iconv转译
          // 解码
          const t = iconv.decode(data,'GBK')
          // 编码
          const r = iconv.encode(t,'utf8')
          d +=r
        })
        proxyRes.on('end',()=>{
          res.send(d)
        })
      }
      const httpProxyMiddleware = require('http-proxy-middleware')
      const proxyConfig = {
        target: 'http://opendata.baidu.com', 
        changeOrigin: true,
        pathRewrite: {
          '^/calendar' : '/',     // 重写请求,
        },
        onProxyReq, // 处理请求
        selfHandleResponse:true, // 手动处理响应
        onProxyRes // 处理响应函数
      }
      const proxy = httpProxyMiddleware.createProxyMiddleware(proxyConfig)
      app.use('/calendar',proxy)
      // app.put('/calendar',(req,res)=>{
      //   res.send('Hello Calendar!')
      // })
      app.listen(port,()=>{
        console.log(`服务器运行在 localhost:${port}/`)
      })
      
    • 效果

在这里插入图片描述

  • 我们使用 exprss + http-proxy-middleware 也完成代理服务器的开发。

    • 同样通用性很差,这里就不继续整了,有兴趣可以自己玩玩。
2.5、收获
  • 了解了代理解决跨域的原理
  • 也知道了webpack代理请求的原理
    • http-proxy-middleware 实现的
  • CORS跨域资源共享,实现过程
  • 还有一点点node的应用

3、webpack代理的理解

在这里插入图片描述

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值