6.1 SpringBoot解决跨域,我推荐这2种超实用方案

news2024/11/25 9:02:10

文章目录

  • 前言
  • 一、验证跨域
    • 1. 添加index.html
    • 2. 增加/auth/test/cors接口
    • 3. IDEA启动多个springboot项目
    • 4. 验证POST方法
    • 5. 验证OPTIONS方法
  • 二、拦截器方案
    • 定义拦截器
    • 注册拦截器,并指定拦截规则
  • 三、过滤器方案
  • 总结
  • 最后


前言

在文章【2-2】中,我和你介绍了支持跨域的最简单方法,即在Controller上增加@CrossOrigin注解。通过这个方法,我们临时支持了跨域,但是这种方法对跨域的支持并不彻底,当前端发送options预检请求时,依然会出现跨域CORS error。
今天这篇文章分享了我使用SpringBoot解决跨域问题的经验和方法,并提供了详细的代码示例和解释,适合对跨域问题不太熟悉的读者。
主要讲解SpringBoot如何解决跨域,首先会验证跨域,然后再给出我推荐的2种超实用解决跨域方案!


一、验证跨域

模拟前端地址:
http://localhost:8080/index.html

后端接口地址:
http://localhost:8081/auth/test/cors

思路:启动两个springboot项目:8080端口和8081端口,在8080端口的静态资源目录index.html中,对8081端口的接口发起请求,因为端口不同,所以属于跨域,从而就可以验证不同请求(POST或OPTIONS等方法)是否报CORS error !

1. 添加index.html

在 tg-book-web的resources下,新建static\index.html

逻辑:使用ajax发送请求,调用8081的/auth/test/cors接口,如果执行成功,将结果输出到id="result"的div下,如下代码是post请求,后面还会改成options请求测试.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
</head>
<body>
<div id="result">
</div>
</body>
<script type="text/javascript">
    $.ajax({
        url: 'http://localhost:8081/auth/test/cors',
        method: 'post',
        success: function (res) {
            console.log(res)
            $('#result').append('<p>' + res['message'] + '</p>')
        }
    })
</script>
</html>

对index.html不进行接口身份认证
因为我们增加了身份认证AuthInterceptor,所以需要在AuthInterceptor的preHandle过滤掉index.html,如下:

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    System.out.println("AuthInterceptor.preHandle:" + request.getServletPath());
    if (request.getServletPath().startsWith("/index.html")) {
        return true;
    }
    ...其它代码
}

2. 增加/auth/test/cors接口

AuthController下增加接口

@PostMapping("/test/cors")
public TgResult<String> testCors() {
    return TgResult.ok();
}

同样,将/auth/test/cors也不进行接口身份认证.
排除身份认证是为了更好的测试跨域,所以同样写在排除"/auth/login"的位置.

registry.addInterceptor(authInterceptor)
                .addPathPatterns("/**")
                // 排除的请求路径
                .excludePathPatterns("/auth/login", "/auth/test/cors");

3. IDEA启动多个springboot项目

关于如何使用IDEA启动多个springboot项目,我就不演示了,我启动了8080和8081两个端口:

在这里插入图片描述

4. 验证POST方法

在浏览器打开 http://localhost:8080/index.html,如下图:

在这里插入图片描述

可以看到,8080/index.html通过ajax请求了8081的接口,返回200,说明支持POST请求跨域!

5. 验证OPTIONS方法

OK,正如我开头所说,@CrossOrigin注解对options方法并没有支持跨域,我们来试一试!

只需将index.html中的 【 method: ‘post’】改为【 method: ‘options’】,重启8080服务,然后用同样的方法测试,如下,报CORS error了 🤣

在这里插入图片描述

详细报错:
在这里插入图片描述


二、拦截器方案

关于拦截器,我在3-3. SpringBoot项目集成【用户身份认证】实战 【全流程篇】基于JWT+双重检查的登录+登出+拦截器 有详细的介绍,SpringBoot中使用拦截器需要2步:

  • 定义拦截器
  • 注册拦截器,并指定拦截规则

定义拦截器

新增一个拦截器类 CorsInterceptor 实现 HandlerInterceptor 接口

package org.tg.book.web.interceptor;

@Component
public class CorsInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 允许跨域源,全部允许为*,否则写允许请求的源ip
        if (StringUtils.isEmpty(request.getHeader("Origin"))) {
            response.setHeader("Access-Control-Allow-Origin", "*");
        } else {
            response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        }
        response.setHeader("Access-Control-Allow-Credentials", "true");
        // 允许跨域方法:全部允许为*,否则写具体的方法,例如:GET, POST, PUT, DELETE, OPTIONS
        response.setHeader("Access-Control-Allow-Methods", "*");
        response.setHeader("Access-Control-Max-Age", "86400");
        // 允许跨域请求头
        response.setHeader("Access-Control-Allow-Headers", "*");

        // 如果是OPTIONS,不走后续操作,直接返回
        if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
            return false;
        }

        return true;
    }
}
  • Access-Control-Allow-Origin: 允许跨域源,全部允许为*,否则写允许请求的源ip。重点:通常会使用白名单机制,只有request.getHeader(“Origin”) 在白名单中才允许跨域。
  • Access-Control-Allow-Methods:允许跨域方法:全部允许为*,否则写具体的方法,例如:GET, POST, PUT, DELETE, OPTIONS

注册拦截器,并指定拦截规则

在原有的InterceptorConfig 中增加注入CorsInterceptor ,并将【跨域拦截器】 放在 【身份认证拦截器】之前,如下:

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Autowired
    private CorsInterceptor corsInterceptor;
    @Autowired
    private AuthInterceptor authInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    	// 新增的跨域拦截器
        registry.addInterceptor(corsInterceptor)
        		.addPathPatterns("/**");
        // 身份认证拦截器
        registry.addInterceptor(authInterceptor)
                .addPathPatterns("/**")
                // 排除的请求路径
                .excludePathPatterns("/auth/login");
    }
}

去除原来的 @CrossOrigin注解,然后验证,同样,完全支持POST和OPTIONS方法!

在这里插入图片描述


三、过滤器方案

新增 CorsFilter 实现Filter接口

public class CorsFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        HttpServletRequest request = (HttpServletRequest) req;
        if (StringUtils.isEmpty(request.getHeader("Origin"))) {
            response.setHeader("Access-Control-Allow-Origin", "*");
        } else {
            response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        }
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods", "*");
        response.setHeader("Access-Control-Max-Age", "86400");
        response.setHeader("Access-Control-Allow-Headers", "*");

        // 如果是OPTIONS,不走后续操作,直接返回
        if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
            return;
        }
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
    }
}

新增配置类CorsConfig 注入 CorsFilter

@Configuration
public class CorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        return new CorsFilter();
    }
}

注意:测试时先注释上面的 跨域拦截器,然后验证,完全支持POST和OPTIONS方法!


总结

今天我使用了两种方案解决跨域:拦截器和过滤器方案,都是超实用的方案。另外,给你留个作业吧,都做成配置扩展,而不是hard code,例如上面说到的白名单机制,实际就是配置一堆ip,其它也是同理,你可以把你的配置扩展写在留言区里,或者遇到的问题在留言区和我一起讨论,感谢你的阅读,也欢迎你把这篇文章分享给更多的朋友一起阅读。

最后

给大家推荐一门专栏课程–>《基于SpringBoot+SpringCloud+Vue前后端分离项目实战》,由我和 前端狗哥 合力打造的一款专栏,可以让你从0到1快速拥有企业级规范的项目实战经验!

具体的优势、规划、技术选型都可以在《开篇》试读!

博主保证会用心持续高质量输出文章哦!

订阅专栏后也可以添加博主的微信,博主会为每一位用户进行针对性指导!

另外,别忘了关注我:天罡gg ,发布新文不容易错过: https://blog.csdn.net/scm_2008

老规矩,请投票给我反馈,谢谢大家的支持!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/552236.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

获取企业服务超市企业信息

地址&#xff1a; 服务机构-苏州工业园区企业服务超市 import os from datetime import datetime from urllib import request import pandas as pd import re import requests from lxml import etree from bs4 import BeautifulSoup import csv import codecs# 20230521 根据…

learn_C_deep_13 (深刻理解宏定义)

目录 宏定义 数值宏常量 字符串宏常量 用定义充当注释符号宏 用 define 宏定义表达式 宏定义中的空格 宏定义 数值宏常量 在C语言中&#xff0c;宏定义可以用于定义数值宏常量。数值宏常量是一个值&#xff0c;在宏定义中用一个常量名称来表示&#xff0c;该值在后续的代…

计算机视觉的应用5-利用PCA降维方法实现简易人脸识别模型

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下计算机视觉的应用5-利用PCA降维方法实现简易人脸识别模型&#xff0c;本文将介绍如何使用主成分分析&#xff08;PCA&#xff09;实现简易的人脸识别模型。首先&#xff0c;我们将简要介绍PCA的原理及其在人脸识别中…

26 KVM热迁移虚拟机

文章目录 26 KVM热迁移虚拟机26.1 总体介绍26.1.1 概述26.1.2 应用场景26.1.3 注意事项和约束限制 26.2 热迁移操作26.2.1 前提条件26.2.2 热迁移脏页率预测&#xff08;可选&#xff09;26.2.3 设置热迁移参数&#xff08;可选&#xff09;26.2.4 热迁移操作&#xff08;共享存…

Linux:文本三剑客之awk

Linux&#xff1a;文本三剑客之awk 一、awk编辑器1.1 awk概述1.2 awk工作原理1.3 awk与sed的区别 二、awk的应用2.1 命令格式2.2 awk常见的内建变量&#xff08;可直接用&#xff09; 三、awk使用3.1 按行输出文本3.2 按字段输出文本3.3 通过管道、双引号调用 Shell 命令 一、a…

【模电实验】日光灯电路及功率因数的提高

实验4 日光灯电路及功率因数的提高 一、实验目的 1&#xff0e;理解提高功率因数的意义并掌握其方法。 2&#xff0e;掌握日光灯电路的联接。 二、原理说明 日光灯电路结构及工作原理 日光灯电路如图4-1所示&#xff0c;日光灯由灯管、镇流器和启辉器三部分组成。 &…

复制带随机指针的链表

&#x1f495;“如果你关注自己已经拥有的&#xff0c;你就会拥有更多。如果你只关注自己没有得到的&#xff0c;你永远不会满足。” - 奥普拉温弗瑞&#x1f495; &#x1f43c;作者&#xff1a;不能再留遗憾了&#x1f43c; &#x1f386;专栏&#xff1a;Java学习&#x1f3…

11. Redis集群(cluster)

11. Redis集群cluster 是什么&#xff1f;能干嘛&#xff1f;集群算法-分片-槽位slot官网出处redis集群的槽位slotredis集群的分片他两的优势slot槽位映射&#xff0c;一般业界有3种解决方案哈希取余分区—致性哈希算法分区3大步骤算法构建一致性哈希环redis服务器IP节点映射k…

【Python sqlite3】零基础也能轻松掌握的学习路线与参考资料

Python sqlite3是Python语言自带的轻量级关系数据库管理系统&#xff0c;它可以让我们在不需要额外的安装和配置下&#xff0c;使用SQLite数据库进行操作和管理。SQLite是一个功能强大的嵌入式数据库&#xff0c;它非常适合在轻量级应用程序中使用&#xff0c;如桌面应用程序、…

ROS学习(4)——launch文件的编写

对于一个复杂的系统,会有十几个、几十个甚至是上百个节点在运行,如果我们每次都是采取“打 开终端、运行 rosrun 指令”来启动应用程序,显得效率非常低。我们需要一个更方便的方式来启动系统。ROS 中提供了“使用 launch 文件 roslaunch”命令来完成系统的启动。具体的实现方法…

Java·Lambda

文章目录 ⚽️1 背景⚽️&#x1f34f;1.1 Lambda表达式的语法&#x1f34f;&#x1f34e;1.2 函数式接口&#x1f34e; &#x1f3c0;2 Lambda表达式的基本使用&#x1f3c0;&#x1f348;2.1 语法精简&#x1f348; &#x1f3c8;3 变量捕获&#x1f3c8;&#x1f3c6;3.1 匿…

Linux Audio (7) DAPM-4 Path/Route添加过程

DAPM-4 Path/Route添加过程 route分类&#xff1a;route转化为Pathcodec驱动add widgetMechine驱动add kcontrol route分类&#xff1a; 常规route {“sink”, NULL, “source”}&#xff0c;其path->connect1 sink widget是Mixer {“Mixer”, name1, “source1”} {“Mixe…

chatgpt赋能Python-python88

Python88 简介 Python88 是一个优秀的 Python 开源库&#xff0c;它提供了许多有用的函数和工具&#xff0c;可用于创建高效的 Web 应用程序&#xff0c;简化数据分析、数据可视化以及机器学习和人工智能任务等。 Python88 帮助开发人员以更简单、更快捷的方式编写代码&#…

VS Code Remote Development

1、Ubuntu 上启用 SSH &#xff08;1&#xff09;打开终端&#xff0c;并且安装openssh-server软件包&#xff1a; sudo apt update sudo apt install openssh-server 当被提示时&#xff0c;输入你的密码并且按 Enter&#xff0c;继续安装。 &#xff08;2&#xff09;安…

RocketMQ消息发送

消息发送示例代码&#xff1a; public static void main(String[] args) throws MQClientException, InterruptedException {DefaultMQProducer producer new DefaultMQProducer("please_rename_unique_group_name");producer.setNamesrvAddr("127.0.0.1:9876…

leetcode-743. 网络延迟时间

1.思路分析&#xff1a; 一道Dijkstra模板题 推荐Dijkstra算法讲解教程 Dijkstra&#xff08;有向图某点到其他所有点的最短路径问题&#xff09; Dijkstra算法的基本思想是贪心策略&#xff0c;每次从未确定最短路径的顶点中选择距离源点最近的一个&#xff0c;然后以该顶点…

Python使用正则表达式

正则表达式&#xff08;Regular Expression&#xff09;&#xff0c;又称规则表达式&#xff0c;是一个计算机科学的概念&#xff0c;通常被用来检索和替换符合某些规则的文本。 1. 正则表达式语法 正则表达式就是记录文本规则的代码。 1. 行定位符 行定位符就是用来描述字…

【STM32G431RBTx】备战蓝桥杯嵌入式→决赛试题→第十二届

文章目录 前言一、题目二、模块初始化三、代码实现interrupt.h:interrupt.c:main.h:main.c: 四、完成效果五、总结 前言 无 一、题目 二、模块初始化 1.LCD这里不用配置&#xff0c;直接使用提供的资源包就行 2.ADC:开启ADCsingle-ended 3.LED:开启PC8-15,PD2输出模式就行了…

MySQL高级(InnoDB引擎)

&#xff08;一&#xff09;逻辑存储结构 表空间&#xff08;ibd文件&#xff09;&#xff0c;会生成ibd文件&#xff0c;一个mysql实例可以对应多个表空间&#xff0c;用于存储记录、索引等数据。 段&#xff0c;分为数据段&#xff08;Leaf node segment&#xff09;、索引段…

学弟研一,有几篇SCI论文,做过前端,读博 or 走开发进国企?

同学你好&#xff0c;在正面先抛开选择就业的方面的问题&#xff0c;其实我觉得生活种的很多选择&#xff0c;都可以从以下的几点进行斟酌与考虑&#xff1a; &#xff08;1&#xff09;你最擅长的是哪个方面&#xff1f;&#xff08;2&#xff09;你的兴趣爱好是在哪个方面&am…