前端解决跨域问题

什么是跨域?

跨域是浏览器行为,是浏览器的一种安全策略;由于浏览器 同源策略 导致浏览器访问服务器时被拦截

同源策略(Sameoriginpolicy):

是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议 (protocol),主机 (host)和端口号(port)

这里还需要提一下http的url组成部分:

HTTP URL 由以下几个部分组成:

1,协议(Scheme):定义因特网服务的类型。最常见的是 http 和 https,分别用于普通和加密的 HTTP 服务。

2,主机(Host):服务器的域名系统 (DNS) 名称或 IP 地址。

3,端口(Port):可选,用于标识服务器上的特定网络程序;如果未指定,默认端口分别是 80 和 443 对应于 http 和 https。

4,路径(Path):服务器上的路径,标识特定资源或服务。

5,查询(Query):可选,发送到服务器的数据;以 ? 开始,可以包含键值对,用 & 分隔。

6,片段(Fragment):可选,用于指导浏览器跳转到页面的特定部分。

以下面地址为例

javascript 复制代码
http://www.example.com:80/path/to/resource?key=value#section

协议:http

主机:www.example.com

端口:80(默认)

路径:/path/to/resource

查询:key=value

片段:section

如下表所示,协议,主机与端口导致的跨域例子

地址一 地址二 是否跨域 跨域原因
https://www.baidu.com http://www.baidu.com https/http协议不同
https://www.baidu.com https://www.example.com 主机不同
https://www.baidu.com https://www.baidu.com:80 端口不同,https默认端口是443,http默认端口是80
https://www.baidu.com https://www.baidu.com:443 端口一致

需要注意的是:跨域不一定都会有跨域问题。

因为跨域问题是浏览器对于ajax请求的一种安全限制:一个页面发起的ajax请求,只能是与当前页域名相同的路径,这能有效的阻止跨站攻击。

因此:跨域问题 是针对ajax的一种限制。

但是这却给我们的开发带来了不便,而且在实际生产环境中,肯定会有很多台服务器之间交互,地址和端口都可能不同,怎么办?

解决跨域问题的方法

1,jsonp解决跨域

JSONP(JSON with Padding) 是 json 的一种"使用模式",可以让网页从别的域名(网站)那获取资料,即跨域读取数据。

原理:

html中的src(获取图片),的href(获取css),script的src(获取javascript)这三个标签都不符合同源策略,它们可以跨域获取数据。JSONP就是通过动态创建

实现封装jsonp的代码:

javascript 复制代码
function jsonp({ url, params, callback }) {
  return new Promise((resolve, reject) => {
    let script = document.createElement("script");
    // 定义一个全局回调函数,若请求返回,则执行里边内容
    window[callback] = function(data) {
        resolve(data);
        document.body.removeChild(script);
    }
    let arrs = [];
    params = { ...params, callback };
    for (let key in params) {
      arrs.push(params[key]);
    }
    script.src = `${url}?${arrs.join("&")}`;
    document.body.appendChild(script);
  });
}
 
jsonp({
  url: "http://localhost:3000/say",
  params: { wd: "Iloveyou" },
  callback: "show"
}).then(data => {
  console.log(data);
});

我们也可以直接在html中使用script标签

html前端代码

javascript 复制代码
<script src="http://localhost:3000/getData?callback=func"><script>
    <script>
      function func(res) {
        alert(res.message + res.name + "你永远" + res.age + "岁");
      }
    </script>

服务端代码:

javascript 复制代码
router.get('/getData', (req, res) => {
  let data = {
    message: 'success!',
    name: '胡不说',
    age: 18
  }
  data = JSON.stringify(data)
  res.end('func(' + data + ')');
});

jsonp的优缺点

优点 缺点
1,不受同源策略限制,实现跨域; 2,使用简单,不需要配置代理 3,兼容性好,低版本ie也能兼容,基本不存在兼容性问题 1,只支持get请求 2,jsonp在调用失败的时候不会返回各种HTTP状态码 3,跳过了同源策略,安全性没办法保证

2,cors头设置

这个一般情况跟前端没啥关系,需要后端去配置放开跨域白名单。

前端发现跨域了

具体沟通情况可参照如下:

javascript 复制代码
前端:后端大大,我这边调您接口好像跨域了,我需要怎么做才能解决这个问题呢?

后端:我现在好忙,你看看能不能自己解决一下

前端:是这样的,我这边配置代理挺麻烦的,不过我先尝试一下(配置不配置再说,复不复杂再说,晚点再沟通)

过了一会。。。

前端:后端大大,我没解决

后端:哦,那我来吧

node设置白名单解决跨域

javascript 复制代码
const http = require('http')
const url = require('url')
// 创建server
const server = http.createServer()
// 定义跨域访问白名单
const authOrigin = ['http://127.0.0.1:5500']

// 监听http请求
server.on('request', (req, res) => {
    const user = { // 模拟返回数据
        id: 1, 
        name: 'zhangsan',
        age: 12
    }
    const origin = req.headers.origin
    if(authOrigin.includes(origin)) {
        // 添加响应头,实现cors
        res.setHeader('Access-Control-Allow-Origin', '*') // 允许所有的地址跨域访问
        //res.setHeader('Access-Control-Allow-Origin', origin) // 只有白名单中的地址才可以跨域访问
    }
    res.end(JSON.stringify(user))
})

// 设置监听端口
server.listen(8081, function() {
    console.log('server is running on 8081 port!')
})

java设置白名单解决跨域

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
@Configuration
public class WebConfig implements WebMvcConfigurer {
 
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**") // 允许跨域的路径
                        .allowedOrigins("*") // 允许跨域请求的域名
                        .allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的请求方法
                        .allowedHeaders("*") // 允许的请求头
                        .allowCredentials(true); // 是否允许证书(cookies)
            }
        };
    }
}

3,代理服务器(Proxy Server)

同源策略是浏览器行为,那我们通过代理服务器访问就不受同源策略的限制了啊,不管是proxy还是nginx代理都是通过这个原理解决的跨域问题

我们以vue3 vite构建项目为例:

1,在项目根目录中的 vite.config.js 文件中,配置代理服务器:

javascript 复制代码
import { defineConfig } from 'vite';
 
export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: 'http://target-domain.com', // 目标服务器地址
        changeOrigin: true, // 是否改变源地址
        rewrite: (path) => path.replace(/^\/api/, '') // 重写路径
      }
    }
  }
});

2,在发送请求时,使用配置好的代理路径(例如 /api):

javascript 复制代码
import axios from 'axios';
 
axios.get('/api/some-endpoint').then(response => {
  console.log(response.data);
});

这样,所有发往 /api 的请求都会被代理到 http://target-domain.com,从而绕过同源策略,实现跨域通信。

4,Websocket

Websocket 是 HTML5 的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。

然后,不会有人用这个方法解决跨域的,Websocket实时通讯有它自己的使用场景,我们需要知道的就是它没有跨域问题就足够了

5,利用iframe标签

这个与jsonp类似,html的iframe标签不受同源策略影响,可以通过它解决跨域;了解有这个方法就行,除非特殊场景,一般也不会用它

总结一下

1,jsonp

2,cors头设置

3,搭建代理服务器

4,Websocket

5,利用iframe标签

整理知识点,基础不牢,地动山摇。

相关推荐
Myli_ing34 分钟前
考研倒计时-配色+1
前端·javascript·考研
余道各努力,千里自同风37 分钟前
前端 vue 如何区分开发环境
前端·javascript·vue.js
软件小伟1 小时前
Vue3+element-plus 实现中英文切换(Vue-i18n组件的使用)
前端·javascript·vue.js
醉の虾1 小时前
Vue3 使用v-for 渲染列表数据后更新
前端·javascript·vue.js
张小小大智慧1 小时前
TypeScript 的发展与基本语法
前端·javascript·typescript
hummhumm1 小时前
第 22 章 - Go语言 测试与基准测试
java·大数据·开发语言·前端·python·golang·log4j
asleep7012 小时前
第8章利用CSS制作导航菜单
前端·css
hummhumm2 小时前
第 28 章 - Go语言 Web 开发入门
java·开发语言·前端·python·sql·golang·前端框架
幼儿园的小霸王2 小时前
通过socket设置版本更新提示
前端·vue.js·webpack·typescript·前端框架·anti-design-vue
疯狂的沙粒2 小时前
对 TypeScript 中高级类型的理解?应该在哪些方面可以更好的使用!
前端·javascript·typescript