后端解决跨域

2023-11-12

对于跨域,相信同学们都有所了解。前端的跨域的若干种方式,大家也都知道,什么 JSONP,iframe+domain 等等。但是我们今天的主题,不是前端跨域,而是后端跨域。

一旦提及到跨域,就会想到同源策略,那我们就先来回顾跨域和同源策略。

什么是跨域请求
首先,我们要了解什么是跨域请求。简单来说,当一台服务器资源从另一台服务器(不同 的域名或者端口)请求一个资源或者接口,就会发起一个跨域 HTTP 请求。举个简单的例子,从http://aaa.com/index.html,发送一个 Ajax 请求,请求地址是 http://bbb.com/下面的一个接口,这就是发起了一个跨域请求。在不做任何处理的情况下,这个跨域请求是无法被成功请求的,因为浏览器基于同源策略 会对跨域请求做一定的限制。那什么又是同源策略呢?

什么是同源策略
首先大家要知道同源策略发生的场景——浏览器中,什么意思呢?如果不是浏览器的话, 就不会受到同源策略的影响。也就是说,两个服务器直接进行跨域请求是可以进行数据请求的。这也就为我们接下来的后端跨域埋下一下小伏笔。 同源策略的目的是什么呢?同源策略限制了从同一个源加载的文档或者脚本如何与来自另 一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。

同源策略限制内容有:
Cookie、LocalStorage、IndexedDB 等存储性内容
DOM 节点
AJAX 请求不能发送
那什么又是同源?

大家都知道,一个域名请求地址的组成是:协议+域名+端口号+请求资源地址 , 当协议、域名、端口号中任意一个不相同时 , 都算作不同源(必须是域名完全相同,比如说 a.example.com 和 b.example.com 这两个域名。虽然它们的顶级域名和二级域名(均为 example.com)都相同,但是三级域名(a 和 b)不相同,所以也不能算作域名相同)。如果不同时满足这上面三个条件,那就不符合浏览器的同源策略。 需要注意的是,不是所有的交互都会被同源策略拦截下来,下面两种交互就不会触发同源策略: 

跨域写操作(Cross-origin writes)
例如超链接、重定向以及表单的提交操 作,特定少数的 HTTP 请求需要添加预检请求(preflight)

跨域资源嵌入(Cross-origin embedding)
<script> 标签嵌入的跨域脚本; o <link> 标签嵌入的 CSS 文件; o <img> 标签嵌入图片;

<video> 和 <audio> 标签嵌入多媒体资源; o <object>, <embed>, <applet> 的插件;

@font-face 引入的字体,一些浏览器允许跨域字体(cross-origin fonts),一些需要同源字体(same-origin fonts);

<frame> 和 <iframe> 载入的任何资源,站点可以使用 X-FrameOptions 消息头来组织这种形式的跨域交互。

这里你或许有个疑问:请求跨域了,那么请求到底发出去没有?

跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。你可能会疑问明明通过表单的方式可以发起跨域请求,为什么 Ajax 就不会?因为归根结底,跨域是为了阻止用户读取到另一个域名下的内容,Ajax 可以获取响应,浏览器认为这不安全,所以拦截了响应。但是表单并不会获取新的内容,所以可以发起跨域请求。同时也说明了跨域并不能完全阻止 CSRF,因为请求毕竟是发出去了。

跨域解决方案
跨域就是通过某些手段来绕过同源策略限制,实现不同服务器之间通信的效果。方法有很多 ,大致分为两类:

服务端进行设置默认允许某些域名跨域访问
从客户端入手想办法绕开同源安全策略
常见的解决方案有:
1.jsonp
利用 <script> 标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的 JSON 数据。JSONP请求一定需要对方的服务器做支持才可以。
JSONP和AJAX相同,都是客户端向服务器端发送请求,从服务器端获取数据的方式。但AJAX属于同源策略,JSONP属于非同源策略(跨域请求)
JSONP优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅支持get方法具有局限性,不安全可能会遭受XSS攻击。
2.cors
CORS(Cross-origin resource sharing),跨域资源共享。CORS 其实是浏览器制定的一个规范,浏览器会自动进行 CORS 通信,它的实现则主要在服务端,它通过一些 HTTP Header 来限制可以访问的域,例如页面 A 需要访问 B 服务器上的数据,如果 B 服务器 上声明了允许 A 的域名访问,那么从 A 到 B 的跨域请求就可以完成。对于那些会对服务器数据产生副作用的 HTTP 请求,浏览器会使用 OPTIONS 方法发起 一个预检请求(preflight request),从而可以获知服务器端是否允许该跨域请求,服 务器端确认允许后,才会发起实际的请求。在预检请求的返回中,服务器端也可以告知客 户端是否需要身份认证信息。我们只需要设置响应头,即可进行跨域请求。

虽然设置 CORS 和前端没什么关系,但是通过这种方式解决跨域问题的话,会在发送请求时出现两种情况,分别为简单请求和复杂请求。

简单请求:
只要同时满足以下两大条件,就属于简单请求:
1)使用GET、HEAD、POST方法之一;
2)Content-Type 的值仅限于:text/plain、multipart/form-data、application/x-www-form-urlencoded,请求中的任意 XMLHttpRequestUpload 对象均没有注册任何事件监听器; XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问;

复杂请求:
不符合以上条件的请求就肯定是复杂请求了。 复杂请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求,该请求是 option 方法的,通过该请求来知道服务端是否允许跨域请求。
我们用PUT向后台请求时,属于复杂请求,后台需被请求的Servlet中添加Header设置,Access-Control-Allow-Origin这个Header在W3C标准里用来检查该跨域请求是否可以被通过,如果值为*则表明当前页面可以跨域访问。默认的情况下是不允许的。

一般我们可以写一个过滤器:

@WebFilter(filterName = "corsFilter", urlPatterns = "/*",
        initParams = {@WebInitParam(name = "allowOrigin", value = "*"),
                @WebInitParam(name = "allowMethods", value = "GET,POST,PUT,DELETE,OPTIONS"),
                @WebInitParam(name = "allowCredentials", value = "true"),
                @WebInitParam(name = "allowHeaders", value = "Content-Type,X-Token")})
public class CorsFilter implements Filter {
 
    private String allowOrigin;
    private String allowMethods;
    private String allowCredentials;
    private String allowHeaders;
    private String exposeHeaders;
 
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        allowOrigin = filterConfig.getInitParameter("allowOrigin");
        allowMethods = filterConfig.getInitParameter("allowMethods");
        allowCredentials = filterConfig.getInitParameter("allowCredentials");
        allowHeaders = filterConfig.getInitParameter("allowHeaders");
        exposeHeaders = filterConfig.getInitParameter("exposeHeaders");
    }
 
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        if (!StringUtils.isEmpty(allowOrigin)) {
            if(allowOrigin.equals("*")){
                // 设置哪个源可以访问
                response.setHeader("Access-Control-Allow-Origin", allowOrigin);
            }else{
                List<String> allowOriginList = Arrays.asList(allowOrigin.split(","));
                if (allowOriginList != null && allowOriginList.size() > 0) {
                    String currentOrigin = request.getHeader("Origin");
                    if (allowOriginList.contains(currentOrigin)) {
                        response.setHeader("Access-Control-Allow-Origin", currentOrigin);
                    }
                }
            }
        }
        if (!StringUtils.isEmpty(allowMethods)) {
            //设置哪个方法可以访问
            response.setHeader("Access-Control-Allow-Methods", allowMethods);
        }
        if (!StringUtils.isEmpty(allowCredentials)) {
            // 允许携带cookie
            response.setHeader("Access-Control-Allow-Credentials", allowCredentials);
        }
        if (!StringUtils.isEmpty(allowHeaders)) {
            // 允许携带哪个头
            response.setHeader("Access-Control-Allow-Headers", allowHeaders);
        }
        if (!StringUtils.isEmpty(exposeHeaders)) {
            // 允许携带哪个头
            response.setHeader("Access-Control-Expose-Headers", exposeHeaders);
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }
 
    @Override
    public void destroy() {
 
    }
}

大功告成,现在前端就可以跨域获取后台的数据了,正如我们上面所说的,后端是实现 CORS 通信的关键。

如果你的SpringBoot版本在2.0以上,以下代码配置即可完美解决你的前后端跨域请求问题:

@Configuration
public class CorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration corsConfiguration = new CorsConfiguration();
        /*是否允许请求带有验证信息*/
        corsConfiguration.setAllowCredentials(true);
        /*允许访问的客户端域名*/
        corsConfiguration.addAllowedOrigin("*");
        /*允许服务端访问的客户端请求头*/
        corsConfiguration.addAllowedHeader("*");
        /*允许访问的方法名,GET POST等*/
        corsConfiguration.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
}

或者使用WebMvcConfigurationSupport,实现方式有很多,感兴趣的可以自行研究。

3. @CrossOrigin注解
这个方法仅对Java有用。springboot中,在Controller类上添加一个 @CrossOrigin(origins ="*") 注解就可以实现对当前controller 的跨域访问了,当然这个标签也可以加到方法上,或者直接加到入口类上对所有接口进行跨域处理,注意这个注解只在JDK1.8版本以上才起作用。
————————————————
版权声明:本文为CSDN博主「CrazySnail_x」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_40910372/article/details/100068498

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

后端解决跨域 的相关文章

  • 一个合格(优秀)的前端都应该阅读这些文章

    的确 有些标题党了 起因是微信群里 有哥们问我 你是怎么学习前端的呢 能不能共享一下学习方法 一句话也挺触动我的 我真的不算是什么大佬 对于学习前端知识 我也不能说是掌握了什么捷径 当然 我个人的学习方法这篇文章已经在写了 预计这周末会在我
  • The overload Pattern

    最近研究有关C 17的书和博客文章时 偶然发现了这种访问模式std variant 使用overload模式 您可以 访问 提供单独的lambda include
  • 如何查看端口占用

    1 Win R 打开 运行 在 运行 中输入cmd 然后按电脑的回车进入命令行 2 输入命令 netstat ano 即可查出所有的端口占用信息 3 netstat ano findstr 端口 可查看具体的端口占用信息 4 查看有效会话
  • 关于B/S结构系统的会话机制(session机制)

    关于B S结构系统的会话机制 session机制 什么是会话 用户打开浏览器 进行一系列操作 最后关闭浏览器 这个整个过程叫做一次会话 真正意义上 会话是session对象的创建到结束的过程 一个session对应一个会话 在服务器端有一个

随机推荐

  • 虚拟机如何进行备份和迁移?

    文章目录 一 虚拟机的备份 方式一 使用VMvare自带的克隆功能 方式二 手动复制 二 虚拟机的迁移 本文以Ubuntu18 04为例 使用的工具是VMware Workstation 16 Pro 即VMware 最新版 Ubuntu1
  • 谷歌直链下载云盘数据集,使用谷歌云盘生成直接下载的url,示例: NeRFStudio-nerfacto默认训练数据集poster下载,nerfstudio 数据集下载链接

    1 如何生成谷歌直链下载云盘数据集的url 谷歌云盘 NeRFStudio nerfacto默认训练数据集poster下载链接 https drive google com uc export download confirm id 1dm
  • 差分与微分

    差分与微分 差分和微分都是数学中的一些基本概念 它们在数学 物理学 经济学和其他领域中都有广泛的应用 1 差分 差分可以理解为函数值之间的差别 特别地 如果一个函数 f f f 在两个相邻点 x 0
  • 【Qt教程】2.6 - Qt5 自定义控件封装

    自定义控件是将原有控件组合 封装 并填入自定义功能 留出适当的函数接口 方便其他控件的调用 设计窗口时 可以将控件拖入 方便模块化设计 GitHub代码仓库 Qt学习例程 1 建立工程 新建自定义控件 创建widget工程 新建控件 Add
  • 青龙面板打不开解决办法

    原因是cdn 自己修改一下就可以了 进入文件夹etc找到hosts etchosts 把这二个添加 104 16 88 20 cdn js delivr net 直接复制在最后一行 就可以解决问题了 或者 1 登陆linux 2 进入青龙容
  • alter database open resetlogs

    问题解决 先要弄清楚alter database open resetlogs是什么意思 为什么要用resetlogs打开数据库 这个命令发出后oracle都做了什么 alter database open resetlogs是要打开数据时
  • pytorch——torch.squeeze() 和torch.unsqueeze()的用法

    torch squeeze torch squeeze 这个函数主要对数据的维度进行压缩 去掉维数为1的的维度 比如是一行或者一列这种 一个一行三列 1 3 的数去掉第一个维数为一的维度之后就变成 3 行 torch squeeze a 就
  • 项目答辩PPT(一)

    以 运动APP 为例展开 1 项目整体介绍 定位和需求 在银行工作的王小姐是个健身爱好者 从制订健身计划 到约人一起跑步 再到周末预订场地和家人打一场羽毛球赛 她用一部手机轻松搞定 现在有了手机APP 运动方便多了 王小姐的运动方式是如今都
  • QT创建右键快捷菜单

    0 目标 在Qcommbobox右键出来菜单 点击BCC校验 自动算出校验值填入编辑框 1 UI界面选择Action editor 新建action 记住对象名 actionBCC 右键action 点击转到槽 选择triggered 点击
  • 指针(一)——指针与二级指针

    一 指针理解 指针是一个变量 用来存放地址的变量 指针变量 是一个变量 是指有一个存储空间 里面放的是指针 变量指针 变量的地址 指针的存在是为了方便计算机的内存管理 经过计算和权衡 我们发现 一个字节给一个地址是比较合适的 在32位的机器
  • 给wangeditor添加上标、下标功能

    我使用的wangeditor没有上标和下标功能 以下是自己添加功能的方法 1 设计功能的函数和原型 Sup menu 构造函数 function Sup editor this editor editor this elem div cla
  • 高性能MySQL实战(一):表结构

    最近因需求改动新增了一些数据库表 但是在定义表结构时 具体列属性的选择有些不知其所以然 索引的添加也有遗漏和不规范的地方 所以我打算为创建一个高性能表的过程以实战的形式写一个专题 以此来学习和巩固这些知识 1 实战 我使用的 MySQL 版
  • 【深度学习】 Python 和 NumPy 系列教程(七):Python函数

    目录 一 前言 二 实验环境 三 Python函数基础 1 定义函数 2 参数传递 3 函数调用 4 返回值 5 函数文档字符串 四 将函数存储在模块中 1 创建模块 2 导入模块 a import 模块名 b from 模块名 impor
  • github.io出现的问题及解决方案

    github io出现的问题及解决方案 个人博客 github io出现的问题及解决方案 1 你的连接不是专用连接 放假回家后打开自己的博客 发现无法打开博客 一开始以为是调样式时不小心搞坏了 打开别人的githunb io博客发现都会出问
  • Python删除字符串中连续重复字符,保留所有去重后字符

    看了很多攻略 但都是全部去除字符串中所有的重复字符或者全部去除字符串所有相邻的重复字符 如果希望得到字符串中相邻字符去重后的全部字符 比如字符串a abbcccd222aaabbbddfff6e 去重后能得到 abcd2abdf6e 那可以
  • CMake中if的使用

    CMake中的if命令用于有条件地执行一组命令 其格式如下 if
  • 判断字符串是否为回文串的Java实现方法(收藏自力扣)

    先把字符串转成字符数组 可以调用toCharArray 方法 public char toCharArray 将此字符串转换为一个新的字符数组 Returns 一种新分配的字符数组 其长度是该字符串的长度 其内容被初始化为包含由该字符串表示
  • 时间序列模型Prophet使用详细讲解

    之前我们已经讲过了如何在Windows系统下安装Python版本的Prophet 详细见这里 接下来的几个部分 我们说下如何使用Prophet 以此来体验下Prophet的丰富内容 内容会比较多 主要翻译自官方文档 教程中使用的数据集可在
  • DAP数据分析平台权限体系说明

    数据对于企业来说是非常重要的 所以产品的安全性需要有所保证 而权限分配就是一种保障方式 通过不同权限查看到不同的数据进行数据隔离 保障数据的安全性 DAP数据分析平台通过授权管理进行角色授权 配置授权 业务授权实现三权分立 不同的用户能看到
  • 后端解决跨域

    对于跨域 相信同学们都有所了解 前端的跨域的若干种方式 大家也都知道 什么 JSONP iframe domain 等等 但是我们今天的主题 不是前端跨域 而是后端跨域 一旦提及到跨域 就会想到同源策略 那我们就先来回顾跨域和同源策略 什么