与浏览器跨域相遇

浏览器的同源策略

浏览器出于对安全的考虑,对同源请求放行,对异源请求限制,这些限制的规则统称为同源策略 ,因此因为同源策略引发的开发问题,称为跨域(异源)问题

同源,两个URL的协议域名端口号都相同,我们称为同源,当三者中任意一项或多项不同,我们就称之为异源

URL URL
http://a.com:80/a https://a.com:80/a
http://a.com:80/a http://b.com:80/a
http://a.com:80/a http://a.com:81/a
http://a.com:80/a http://a.com:80/a/b

同源请求

浏览器对标签发出请求进行轻微的限制,对ajax进行严厉的限制

浏览器对Ajax的限制

跨域方案

解决跨域的方式有很多,现在主流的就是CORS代理两种,根据具体的生产环境我们酌情选择。

生产环境存在跨域问题

第一种情况是前端打包成静态资源,然后上传到静态服务器,通过一个域名来访问(a.com),在运行过程中,我们的js可能需要通过ajax去请求一些数据资源,但是数据服务器的访问域名是另外的(b.com),这就引发了跨域问题。同时也是说明生产环境下是存在跨域问题。这种情况下,我们就不需要考虑开发环境是怎么样的,首先是要解决生产环境的开发问题,那么只有通过JSONPCORS ,现在主流的,同时也是官方推荐的解决方案就是CORS

使用CORS解决跨域问题

CORS 是现在跨域问题最正统,也是官方最推荐的解决方案。浏览器检验跨域的规则也称之为CORS规则,我们要解决跨域问题,就是要让它通过校验。

CORS规则

CORS是一套机制,用于浏览器校验跨域请求,他的基本理念是:

  1. 只要服务器明确表示允许,则校验通过
  2. 服务器明确拒绝或者没有表示,则校验不通过

从上所述,我们明确了要通过CORS解决跨域问题,必须要后端解决,同时要保证服务器是"自己人"。

CORS 将请求分为简单请求预检请求

简单请求
  • 请求方式为GETHEADPOST
  • 头部字段满足CORS安全规范
  • Content-Type 标头所指定的媒体类型的值仅限于下列三者之一:
    • text/plain
    • multipart/form-data
    • application/x-www-form-urlencoded
预检请求

预检请求 在请求前先会发送一个请求方式为OPTIONS 的预检请求,询问请求头、请求源、请求方式是否允许,通过后再和简单请求相同发送

生产环境不存在跨域问题

另一种情况,前端开发打包上传到一个静态资源目录,后端程序也搭建好了一个服务器,但是在这两者之间存在一个反向代理,这意味着用户在请求的时候使用的是同一个域名,例如,a.com 访问的是前端页面资源,但是 a.com/api/xxx 就被代理服务器转发到了道理服务器,也就意味着生产环境是不存在跨域问题的,那么就不需要后端使用CORS,这种情况就需要前端在开发服务器中配置代理

使用代理解决跨域问题

Express框架

js 复制代码
const express = require('express')
 // const proxy = require('express-http-proxy')
const app = express() //创建服务器

//接受对目标 的GET请求
app.get('目标请求', async (req,res) => {
     // 使用CORS解决对代理服务器的跨域
     res.header('access-control-allow-origin', '*');
      // 相应一段文本
      res.send('hello,world')
 })

 // 监听8080端口
 app.listen(8080, () => {
     console.log('服务器已启动')
 })

HTTP模块

js 复制代码
let http = require('http');
let fs = require('fs');

// 代理配置
let conifg = {
    '/': { // 需要拦截的本地请求路径
        target: 真实代理地址, // 真实代理地址
        port: 80, // 端口,默认80
    }
};

// 创建http服务
let app = http.createServer(function (request, response) {
    let url = request.url === '/' ? 'index.html' : request.url;
    // response.setHeader('Access-Control-Allow-Origin', '*');
    // response.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE'); // If needed
    // response.setHeader('Access-Control-Allow-Headers', 'Authorization,Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers,RequestID,X-Content-Type-Options,X-Content-Type-Options,X-Frame-Options,X-Powered-By,X-Version,x-xss-protection,Strict-Transport-Security') // If needed
    // response.setHeader('Access-Control-Allow-Credentials', true); 


    // 存在代理地址,走代理请求
    if (hasProxy(url, request, response)) {
        return;
    }

    // 普通请求和资源加载
    fs.readFile(__dirname + url, function (err, data) {
        if (err) {
            console.log('请求失败', err);
        } else {
            response.end(data);
        }
    });
});

// 判断是否存在代理地址
function hasProxy(url, request, response) {
    for (const key in conifg) {
        if (url.indexOf(key) < 0) {
            continue;
        }
        const { target, port } = conifg[key];
        let info = target.split('//');
        let opts = { // 请求参数
            protocol: info[0],
            host: info[1],
            port: port || 80,
            method: request.method,
            path: url,
            json: true,
            headers: {
                ...request.headers,
                host: info[1],
                referer: target + url,
            }
        }
        proxy(opts, request, response);
        return true;
    }
    return false;
}

// 代理转发
function proxy(opts, request, response) {
    // 请求真实代理接口
    var proxyRequest = http.request(opts, function (proxyResponse) {
        // 代理接口返回数据,写入本地response
        proxyResponse.on('data', function (chunk) {
            console.log('chunk', chunk, opts)
            response.write(chunk, 'binary');
            // response.header('access-control-allow-origin', '*');

        });
        // 代理接口结束,通知本地response结束
        proxyResponse.on('end', function () {
            response.end();
        });
        response.writeHead(proxyResponse.statusCode, proxyResponse.headers);
        // console.log(response,'proxy')
    });

    // 本地接口数据传输,通知代理接口请求
    request.on('data', function (chunk) {
        console.log(chunk, 777)
        proxyRequest.write(chunk, 'binary');
    });

    // 本地请求结束,通知代理接口请求结束
    request.on('end', function (data) {
        proxyRequest.end();
    });
}

app.listen(8080);
console.log('server is listen on 8080....');
相关推荐
耶啵奶膘5 分钟前
uniapp-是否删除
linux·前端·uni-app
王哈哈^_^2 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
cs_dn_Jie2 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic3 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿3 小时前
webWorker基本用法
前端·javascript·vue.js
cy玩具3 小时前
点击评论详情,跳到评论页面,携带对象参数写法:
前端
清灵xmf4 小时前
TypeScript 类型进阶指南
javascript·typescript·泛型·t·infer
小白学大数据4 小时前
JavaScript重定向对网络爬虫的影响及处理
开发语言·javascript·数据库·爬虫
qq_390161774 小时前
防抖函数--应用场景及示例
前端·javascript
334554325 小时前
element动态表头合并表格
开发语言·javascript·ecmascript