Spring Boot三种跨域解决方案与Spring Security跨域解决方案

news2024/9/27 9:29:58

跨域解决方案

  • 1、什么是跨域
  • 2、Spring Boot跨域解决方案
    • 1、服务端代码
    • 2、前端页面
    • 3、运行服务
  • 3、Spring Security跨域解决方案

1、什么是跨域

什么是跨域,首先可以参考我之前写的这篇文章:JavaWeb跨域问题及解决方案 ,另外我下面会做补充。

很多人误认为资源跨域时无法请求,实际上,通常情况下请求是可以正常发起的(注意,部分浏览器存在特例),后端也正常进行了处理,只是在返回时被浏览器拦截,导致响应内容不可使用。此外,我们平常所说的跨域实际上都是在讨论浏览器行为。

CORS(Cross-Origin Resource Sharing)的规范中有一组新增的HTTP首部字段,允许服务器声明其提供的资源允许哪些站点跨域使用。通常情况下,跨域请求即便在不被支持的情况下,服务器也会接收并进行处理,在CORS的规范中则避免了这个问题。浏览器首先会发起一个请求方法为OPTIONS 的预检请求,用于确认服务器是否允许跨域,只有在得到许可后才会发出实际请求。此外,预检请求还允许服务器通知浏览器跨域携带身份凭证(如cookie)。

CORS常见首部字段:

  • Access-Control-Allow-Origin:具体的站点或*,如果需要浏览器发起请求时携带凭证信息则不允许为*,如果为具体的站点则响应头的Vary字段需要携带Origin属性
  • Access-Control-Allow-Methods:仅在预检请求的响应中指定有效,用于表明服务器允许跨域的HTTP方法,多个方法之间用逗号隔开
  • Access-Control-Allow-Headers:仅在预检请求的响应中指定有效,用于表明服务器允许携带的首部字段。多个首部字段之间用逗号隔开
  • Access-Control-Max-Age:指明本次预检请求的有效期,单位为秒。在有效期内,预检请求不需要再次发起
  • Access-Control-Allow-Credentials:为true时,浏览器会在接下来的真实请求中携带用户凭证信息(cookie等),服务器也可以使用Set-Cookie向用户浏览器写入新的cookie

简单请求:

  • 定义:不携带自定义请求头信息的GET请求、HEAD请求,以及 Content-Type为application/x-www-form-urlencoded、multipart/form-data或text/plain的POST请求
  • 浏览器在发起请求时,会在请求头中自动添加一个 Origin 属性,值为当前页面的 URL 首部。当服务器返回响应时,若存在跨域访问控制属性,则浏览器会通过这些属性判断本次请求是否被允许
  • 只需后端在返回的响应头中添加Access-Control-Allow-Origin字段并填入允许跨域访问的站点即可

预检请求:

  • 会发送一个 OPTIONS 请求到目标站点,以查明该请求是否安全,防止请求对目标站点的数据造成破坏
  • 定义:以 GET、HEAD、POST 以外的方法发起;或者使用POST方法,但请求数据为application/x-www-form-urlencoded、multipart/form-data和text/plain以外的数据类型;再或者,使用了自定义请求头,则都会被当成预检请求类型处理

带凭证的请求:

  • 定义:携带了用户cookie等信息的请求
  • jquery ajax设置方法:xhrFields : {withCredentials : true}
  • 浏览器在实际发出请求时,将同时向服务器发送 cookie,并期待在服务器返回的响应信息中指明 Access-Control-AllowCredentials为true,否则浏览器会拦截,并抛出错误

2、Spring Boot跨域解决方案

下面介绍Spring Boot三种跨域解决方案:

  • (配置单个Controller)@CrossOrigin注解:dispatcherServlet中处理,原理是CorsInterceptor
  • (全局配置)重写WebMvcConfigurer.addCorsMappings方法:dispatcherServlet中处理,原理是CorsInterceptor
  • (推荐)CorsFilter:由filter处理,要早于前两种

Spring MVC的请求顺序一般是:浏览器->filter->DispatcherServlet->interceptor->controller,因此用CorsFilter会更早地去处理跨域,效果也更好。

配置CorsFilter如下代码所示。

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import java.util.Arrays;

@Configuration
public class CrossoriginConfig {
    @Bean
    public FilterRegistrationBean<CorsFilter> corsFilter(){
        FilterRegistrationBean<CorsFilter> registrationBean = new FilterRegistrationBean<>();
        CorsConfiguration corsConfig = new CorsConfiguration();
        // cors配置
        // 后端服务器(http://localhost:8182)为资源提供方,AllowedOrigins可配置哪些网站可以获取我的资源
        corsConfig.setAllowedOrigins(Arrays.asList("http://localhost"));
        //corsConfig.setAllowedOrigins(Arrays.asList("*"));
        // 预检请求的响应中有效
        corsConfig.setAllowedMethods(Arrays.asList("GET","POST"));
        corsConfig.setAllowedHeaders(Arrays.asList("Content-Type"));
        //corsConfig.setMaxAge(Duration.ofHours(1L));
        corsConfig.setAllowCredentials(true);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**",corsConfig);
        registrationBean.setFilter(new CorsFilter(source));
        registrationBean.setOrder(-1);
        return registrationBean;
    }
}

1、服务端代码

import org.springframework.web.bind.annotation.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

@RestController
@RequestMapping("/user")
public class UserController {
    @GetMapping("/getName")
    public String getName(){
        return "用户的名称为张三";
    }

    @PostMapping("/save")
    public String save(@RequestBody Map<String,String> user, HttpServletRequest request, HttpServletResponse response){
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
        System.out.println("username:"+user.get("username"));
        System.out.println("password:"+user.get("password"));

        // 测试跨域携带cookie
        boolean hasMyCookie = false;
        if(null != request.getCookies() && request.getCookies().length > 0){
            for (Cookie cookie : request.getCookies()) {
                if("sb-co".equals(cookie.getName())){
                    hasMyCookie = true;
                }
                System.out.println(cookie.getName()+":"+cookie.getValue());
            }
        }
        if(!hasMyCookie){
            Cookie cookie = new Cookie("sb-co", format.format(new Date()));
            cookie.setMaxAge(3600);
            response.addCookie(cookie);
        }
        return "保存用户成功";
    }
}

2、前端页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="jquery-3.4.1.js"></script>
    <script>
        $(function () {
            $("#getName").click(function () {
                $.ajax({
                    url: "http://localhost:8182/user/getName",
                    method : "get",
                    success:function (result) {
                        alert(result)
                    }
                });
            });
            $("#saveUser").click(function () {
                $.ajax({
                    url: "http://localhost:8182/user/save",
                    method : "post",
                    contentType : "application/json;charset=UTF-8",
                    data : JSON.stringify({
                        "username":"孙悟空"
                    }),
                    xhrFields : {withCredentials : true},
                    success:function (result) {
                        alert(result)
                    }
                });
            });
        });
    </script>
</head>
<body>
    <button id="getName">简单请求-获取用户名称</button>
    <button id="saveUser">预检请求:保存用户</button>
</body>
</html>

3、运行服务

将静态资源文件放到某个目录下,然后在nginx.conf中配置location,如下所示。

# server模块下;root为静态资源存放目录
server {
        listen       80;
        location / {
            root   html/SpringMvc;
            index  index.html index.htm;
        }
}

然后启动nginx,作为静态资源服务器。

启动Spring Boot。

浏览器访问http://localhost ,如下图所示。
在这里插入图片描述
分别测试简单请求和预检请求,观察浏览器控制台Network,如下图所示。
在这里插入图片描述
preflight就是预检请求。状态都是200,说明跨域是OK的。

通过UserController.save方法的代码可知,第一次请求时会response会携带cookie “sb-co”,这是一个跨域cookie。当我们再次请求时,由于我们配置了允许跨域cookie,请求里会携带“sb-co”,后端控制台也能打印该cookie的值,如下图所示。
在这里插入图片描述

3、Spring Security跨域解决方案

引入spring security框架后,第1、2种方案都会失效,第3种方案如果过滤器的优先级要低于spring security的优先级也会失效。

如果配置的是第1、2种方案或低优先级filter的第3种方案,预检请求会先到达spring security的过滤器,由于预检请求不会携带任何凭证信息,因此会被拦截下来。

spring security解决方案:

  • 使用第1、2种方案或低优先级filter的第3种方案:需要让spring security对options类型的预检请求(不会携带凭证)放行
  • 使用高优先级的filter:优先级必须高于spring security过滤器链中最高优先级的过滤器
  • 专业解决方案:开启cors

以下有三种方式可以开启spring security的cors:

  • 手动指定一个CorsConfigurationSource实例
  • 声明一个CorsFilter的Spring Bean
  • 将CorsConfigurationSource实例声明为Spring Bean,不用手动指定

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

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

相关文章

unreal中actor的property replicate简单小节

首先参考的网址是官网中的&#xff1a; https://docs.unrealengine.com/5.1/en-US/multiplayer-programming-quick-start-for-unreal-engine/ unreal引擎的版本是5.1 还原的过程相对比较简单&#xff0c;主要的精力花在了编译报错和调试的过程。 属性复制的流程如下&#xff…

尚医通-MongDB简介-安装-概念-操作(十六)

目录 &#xff08;1&#xff09;MongDB-简介和安装 &#xff08;2&#xff09;MongDB-概念和操作 &#xff08;1&#xff09;MongDB-简介和安装 1、NoSQL简介 MongDB为什么要用呢&#xff1f;之前我们用MySQL数据库&#xff0c;如果数据达到一定的量级&#xff0c;或者有高…

QT 之SQLite数据库

文章目录一、windows下使用命令行方法操作db文件1、 安装sqlite2、 使用sqlite二、QT操作sqlite1、 建立数据库2、 打开数据库3 、关闭数据库4、 创建数据表5 、插入数据6、 删除表数据7、 修改表数据8、 查询数据——遍历查询、条件查询一、windows下使用命令行方法操作db文件…

计算机网络期中考试部分题目

1. &#xff08;1&#xff09;以太网帧的数据部分是IP数据报&#xff0c;只要数出相应字段所在的字节即可。由图可知以太网帧头部有6&#xff0b;6214字节&#xff0c;IP数据报首部的目的P地址字段前有4x4 16字节&#xff0c;从帧的第1字节开始数141630字节&#xff0c;得目的P…

java怎么完成输出语句

我们在前面的文章和案例中&#xff0c;其实早就知道Java是如何实现输出功能的了。没错&#xff0c;就是利用System.out.println()语句来实现的&#xff01;接下来壹哥就给大家详细解读一下这个语句的作用及其含义。 1. System 我们先来看看System是个什么东东。 System是Jav…

不推荐别的了,IDEA 自带的数据库工具就很牛逼!

MySQL 等数据库客户端软件市面上非常多了&#xff0c;别的栈长就不介绍了&#xff0c; 其实 IntelliJ IDEA 自带的数据库工具就很牛逼&#xff0c;不信你继续往下看。 本文以 IntelliJ IDEA/ Mac 版本作为演示&#xff0c;其他版本的应该也差距不大&#xff01; 1、打开数据库…

JPE驱动器维修印刷机驱动器维修JV4-380-1410

印刷机水辘/墨辘/墨斗辊电机马达驱动器维修、墨控电机驱动器维修、JPE驱动器维修。 三菱印刷机电路板维修范围&#xff1a;东洋变频器维修&#xff0c;油墨电机维修&#xff0c;水辊变频器维修&#xff0c;电眼控制板维修&#xff0c;接口板维修&#xff0c;电源维修等。 小森…

【结构型】外观模式(Facade)

目录外观模式(Facade)适用场景外观模式实例代码&#xff08;Java&#xff09;外观模式(Facade) 为子系统中的一组接口提供一个一致的界面&#xff0c;外观模式 (Facade) 定义了一个高层接口&#xff0c;这个接口使得这一子系统更加容易使用。 适用场景 要为一个复杂子系统提…

【C++】初级面试整理

C基础 四种类型转换 static_cast&#xff1a;用于良性转换&#xff0c;一般不会导致意外发生&#xff0c;风险很低。常用于基本类型转换到 void&#xff0c;转换父类指针到子类不安全&#xff1b; const_cast&#xff1a;一般用于去掉const属性以及volatile&#xff0c;但是…

如何进行深度数据恢复?分享详细的恢复方法

有时我们会发现保存已久的照片&#xff0c;因为某些误操作导致它们消失了。通过多种方法都没有办法找回&#xff0c;这时该怎么办&#xff1f;不妨尝试下深度数据恢复的方法。它可以帮助我们找到更深层次的数据&#xff01; 一、恢复数据前的一些问题 很多人都会遇到数据丢失的…

windows环境下安装RocketMQ

文章目录前言一、下载二、环境变量配置三、启动RocketMq1.启动nameserver2.启动broker四、RocketMq控制台安装1.下载2.修改配置文件3.打包4.启动前言 环境要求&#xff1a;JDK1.8Maven 3.2x;64为操作系统 一、下载 官网下载地址&#xff1a;https://rocketmq.apache.org/zh/…

xxljob 的路由策略如何理解,他的选择逻辑是什么(小白阅读)

目录 1 需求2 路由策略2.1 第一个 FIRST2.2 最后一个 LAST2.3 ROUND 轮询2.4 RANDOM :随机2.5 CONSISTENT_HASH:一致性HASH2.6 LEAST_FREQUENTLY_USED:最不经常使用2.7 LEAST_RECENTLY_USED :最近最久未使用2.8 FAILOVER :故障转移2.9 BUSYOVER :忙碌转移1 需求 xxljob …

Ansible变量定义和使用

剧本中使用变量 在playbook中,可以直接定义变量,如下所示: vars&#xff1a;变量名变量值&#xff0c;也可以写在下一级将改成冒号空格值的形式 debug&#xff1a;debug模块&#xff0c;msg消息 引用变量&#xff1a;使用两对{{ }} vim juben.yml - hosts: allvars:bianliang…

extern关键字以及加了头文件引用的作用

0.前言 xdm&#xff0c;今天是阳了的第一天&#xff0c;昨天是高烧&#xff0c;浑身酸痛&#xff0c;今天好多了。。。祝大家健康。 推荐一首歌《不是因为寂寞才想你》。看了《阿凡达2》&#xff0c;感觉没第一部好看哎。 1.extern 之前写过一篇博客extern关键字。今天讲一下和…

fpga实操训练(ip ram和ip fifo)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 所有的fpga ip当中&#xff0c;用的最多的ip一般有pll、rom、ram和fifo。前面&#xff0c;我们讨论过了rom&#xff0c;rom相比较ram和fifo而言&am…

弹性盒子(flex)

一、什么是弹性盒子 弹性盒子是一种用于按行或按列布局元素的一维布局方法。元素可以膨胀以填充额外的空间&#xff0c;收缩以适应更小的空间。 二、flex 模型说明 主轴&#xff08;main axis&#xff09;&#xff1a; 是沿着 flex 元素放置的方向延伸的轴&#xff08;比如页…

【vue】关于vuex的一点补充

1.vuex的基本和下载 1.vuex是一种集中管理模式&#xff0c;举个详细一点的例子就是公共的数据&#xff0c;函数和计算属性&#xff0c;允许任何组件来使用&#xff0c;修改这里面的数据&#xff0c;vuex也可以成为store处理模式&#xff0c;其中一个store示例有state&#xff…

hadoop大数据入门HDFS、MapReduce、YARN的个人通俗理解

大数据时代,在数据量,计算量,计算时间上都是单机无法胜任的&#xff0c;通过简单的增强单机已经无法解决。普遍的解决方案为将多个单机组合起来进行存储和计算的分布式集群来处理。 Hadoop支持使用普通机器组成可拓展的分布式主从集群实现了对大数据的分布式存储&#xff08;HD…

SpringCloud之Hystrix服务熔断

Hystrix服务熔断1. 服务雪崩2. Hystrix 的概念3. Hystrix 的作用4. 服务熔断4.1 概念4.2 服务熔断解决哪些问题&#xff1f;4.3 案例5. 服务降级5.1 概念5.2 案例6. 服务熔断和降级的区别7. Dashboard 流监控分布式系统面临的问题复杂分布式体系结构中的应用程序有数十个依赖关…

At and Cron and Scheduling task

1.at 一次性任务 一次性使用&#xff0c;执行某条命令或者脚本&#xff0c;守护进程atd,默认安装以及开机启动。在输入完命令的时候&#xff0c;按CtrlD结束 now 5min 现在过后5分钟 teatime tomorrow 明天的下午茶时间-4点 noon 中午 5pm august 3 2016 2016年八月3号的下午5点…