与浏览器跨域相遇

浏览器的同源策略

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

同源,两个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....');
相关推荐
霍先生的虚拟宇宙网络18 分钟前
webp 网页如何录屏?
开发语言·前端·javascript
温吞-ing20 分钟前
第十章JavaScript的应用
开发语言·javascript·ecmascript
彪82521 分钟前
第十章 JavaScript的应用 习题
javascript·css·ecmascript·html5
jessezappy39 分钟前
jQuery-Word-Export 使用记录及完整修正文件下载 jquery.wordexport.js
前端·word·jquery·filesaver·word-export
旧林8431 小时前
第八章 利用CSS制作导航菜单
前端·css
yngsqq1 小时前
c#使用高版本8.0步骤
java·前端·c#
Myli_ing2 小时前
考研倒计时-配色+1
前端·javascript·考研
余道各努力,千里自同风2 小时前
前端 vue 如何区分开发环境
前端·javascript·vue.js
PandaCave2 小时前
vue工程运行、构建、引用环境参数学习记录
javascript·vue.js·学习
软件小伟2 小时前
Vue3+element-plus 实现中英文切换(Vue-i18n组件的使用)
前端·javascript·vue.js