跨域的解决方式(java后端)

文章目录

  • 一、跨域介绍
    • 1、什么是跨域
    • 2、为什么会产生跨域
    • 3、禁止跨域的原因
  • 二、简单请求和非简单请求
    • 1、简单请求
      • 1.1、什么时简单请求
      • 1.2、简单请求基础流程
    • 2、非简单请求
      • 2.1、预检请求
      • 2.2、预检请求的回应
      • 2.3、浏览器的正常请求和回应
    • 3、自定义跨域过滤器
  • 三、解决方式
    • 1、@CrossOrigin注解
    • 2、CorsRegistry方式
    • 3、CorsFilter过滤器
      • 3.1、源码解析
    • 4、CorsWebFilter网关WebFlux过滤器

一、跨域介绍

1、什么是跨域

  • 同源策略(Sameoriginpolicy)是浏览器最核心也最基本的安全功能
  • 一个页面发起的ajax请求,只能是与当前页域名相同的路径,这能有效的阻止跨站攻击
  • 所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号

2、为什么会产生跨域

  • 前后端分离模式下,客户端请求前端服务器获取视图资源
  • 客户端向后端服务器获取数据资源
  • 前端服务器的协议,IP和端口和后端服务器不一样,就产生了跨域

在这里插入图片描述

3、禁止跨域的原因

在这里插入图片描述

二、简单请求和非简单请求

  • CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)
  • 它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制
  • 浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)

1、简单请求

1.1、什么时简单请求

只要同时满足以下两大条件,就属于简单请求

  • 请求方法是以下三种方法之一
    • HEAD
    • GET
    • POST
  • HTTP的头信息不超出以下几种字段
    • Accept
    • Accept-Language
    • Content-Language
    • Last-Event-ID
    • Content-Type:只限制三个值
      • application/x-www-form-urlencoded
      • multipart/form-data
      • text/plain

1.2、简单请求基础流程

  • 浏览器发现这次跨源AJAX请求是简单请求,就自动在头信息之中,添加一个Origin字段
  • Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求

举例

  • HTTP请求的方法是GET,响应response添加自定义头信息X-Name
  • 下面是这个请求的HTTP头信息

在这里插入图片描述

  • 如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应
    • 浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段(详见下文)
    • 从而抛出一个错误,但是这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200

在这里插入图片描述

  • 如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段
    • 有四个与CORS请求相关的字段,都以Access-Control-开头

在这里插入图片描述

  1. Access-Control-Allow-Origin
    • 该字段是必须的
    • 要么是请求时Origin字段的值
    • 要么是一个*,表示接受任意域名的请求
  2. Access-Control-Allow-Credentials
    • 该字段可选
    • 它的值是一个布尔值,表示是否允许发送Cookie
    • 默认情况下,Cookie不包括在CORS请求之中
    • 设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器
    • 另外一方面,前端AJAX请求必须设置withCredentials = true
    • 注意:如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名
  3. Access-Control-Expose-Headers
    • 该字段可选
    • CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma
    • 前端响应如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定

简单请求响应跨域设置

前端
在这里插入图片描述
后端
在这里插入图片描述

2、非简单请求

2.1、预检请求

  • 非简单请求是那种对服务器有特殊要求的请求
    • 比如请求方法是PUT或DELETE
    • 或者Content-Type字段的类型是application/json
  • 非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求
    • 浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段
    • 只有得到肯定答复,浏览器才会发出正式的请求,否则就报错

举例

  • HTTP请求的方法是POST,携带json请求体并添加自定义头信息X-Data
  • 下面是这个"预检"请求的HTTP头信息

在这里插入图片描述

  • "预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪个源
  • Access-Control-Request-Method
    • 该字段是必须的
    • 用来列出浏览器的CORS请求会用到哪些HTTP方法
  • Access-Control-Request-Headers
    • 该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段

2.2、预检请求的回应

  • 如果服务器否定了"预检"请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段
    • 浏览器就会认定,服务器不同意预检请求
    • 控制台会打印出如下的报错信息

在这里插入图片描述

  • 如果服务器收到"预检"请求以后,检查了OriginAccess-Control-Request-MethodAccess-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应

在这里插入图片描述

  • 关键的还是Access-Control-Allow-Origin字段
    • 表示http://localhost:8081可以请求数据
    • 该字段也可以设为星号,表示同意任意跨源请求
  • Access-Control-Allow-Methods
    • 该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法
    • 注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求
  • Access-Control-Max-Age
    • 该字段可选,用来指定本次预检请求的有效期,单位为秒
    • 在此期间,不用发出另一条预检请求
  • Access-Control-Allow-Headers
    • 该字段可选,默认情况只有几个固定请求头可以发送
    • 如果前端需要发送其他请求头,就必须在Access-Control-Allow-Headers里面指定

2.3、浏览器的正常请求和回应

  • 一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段
  • 服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段

"预检"请求之后,浏览器的正常CORS请求

  • 头信息的Origin字段是浏览器自动添加的

在这里插入图片描述

服务器正常的回应

  • Access-Control-Allow-Origin字段是每次回应都必定包含的

在这里插入图片描述

非简单请求响应跨域设置

前端

在这里插入图片描述

后端
在这里插入图片描述

3、自定义跨域过滤器

@Component
public class CrosFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        HttpServletRequest request =(HttpServletRequest) servletRequest;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "*");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "*");
        /**
         * 如果设置Access-Control-Allow-Credentials,允许跨域请求携带cookie
         * 那么Access-Control-Allow-Origin不能为*,只能为指定的域名
         */
        // response.setHeader("Access-Control-Allow-Credentials", "true");
        // 非预检请求,放行即可,预检请求,则到此结束,不需要放行
        if (!"OPTIONS".equalsIgnoreCase(request.getMethod())) {
            filterChain.doFilter(servletRequest, servletResponse);
        }
    }
}

三、解决方式

1、@CrossOrigin注解

@CrossOrigin源码

  • @CrossOrigin可以用在被RequestMapping修饰的方法Controller类上
  • 添加此注解即可实现跨域请求
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrossOrigin {

	@AliasFor("origins")
	String[] value() default {};

	@AliasFor("value")
	String[] origins() default {};

	/**
	 * @since 5.3
	 */
	String[] originPatterns() default {};

	String[] allowedHeaders() default {};

	String[] exposedHeaders() default {};

	RequestMethod[] methods() default {};
	
	String allowCredentials() default "";

	long maxAge() default -1;
}

@CrossOrigin属性介绍

  • origins和value
    • 支持的源,origins和value都是相同的配置,互为别名,默认配置是“*”
    • 表示服务器支持所有源的跨域请求,安全信息较低
    • 最好根据实际情况设置对应的信息(协议 + 域名 + 端口)
  • originPatterns
    • 同样表示支持的源,Spring 5.3 引入的属性,默认为空
    • 与origins二选一,该字段为list,也就是可以配置多个
  • allowedHeaders
    • 允许跨域的请求头信息,默认为“*”表示允许所有的请求头
    • 默认前端可以发送的请求头:Cache-Control、Content-Language、Expires、Last-Modified、Pragma
  • exposedHeaders
    • 服务器允许客户端获取的响应头,默认为空
    • 默认前端可以获取的响应头:Cache-Control、Content-Language、Expires、Last-Modified、Pragm
  • methods
    • 服务器允许的Http Request类型,默认是允许GET、POST、HEAD
  • allowCredentials
    • 浏览器是否需要把凭证(如:cookies、CSRF tokens)发送到服务器,默认是关闭的
    • 该选项开启后会与配置的源建立高度信任的关系,并且还会暴露一些敏感信息,所以开启该选项时origin不允许设置为“*”
  • maxAge
    • “预检”结果的缓存时间,单位是秒
    • 默认1800s,在缓存时间内同一请求不需要“预检”请求

@CrossOrigin注解比较适用于较细粒度的跨域控制

2、CorsRegistry方式

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                //是否发送Cookie
                // .allowCredentials(true)
                //放行哪些原始域
                .allowedOrigins("*")
                .allowedMethods("*")
                .allowedHeaders("*")
                .exposedHeaders("*");
    }
}

3、CorsFilter过滤器

  • 如果设置Access-Control-Allow-Credentials为true,允许跨域请求携带cookie
  • 但是Access-Control-Allow-Origin不能为*,只能为指定的域名
@Configuration
public class GlobalCorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        // 1. 添加 CORS配置信息
        CorsConfiguration config = new CorsConfiguration();
        // 放行哪些原始域
        config.addAllowedOrigin("*");
        // 是否发送 Cookie
        // config.setAllowCredentials(true);
        // 放行哪些请求方式
        config.addAllowedMethod("*");
        // 放行哪些原始请求头部信息
        config.addAllowedHeader("*");
        // 暴露哪些头部信息
        config.addExposedHeader("*");
        // 2. 添加映射路径
        UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
        corsConfigurationSource.registerCorsConfiguration("/**", config);
        // 3. 返回新的CorsFilter
        return new CorsFilter(corsConfigurationSource);
    }
}

3.1、源码解析

CorsFilter过滤器

在这里插入图片描述

DefaultCorsProcessor

在这里插入图片描述

DefaultCorsProcessor类的handleInternal方法

protected boolean handleInternal(ServerHttpRequest request, ServerHttpResponse response,
		CorsConfiguration config, boolean preFlightRequest) throws IOException {

	String requestOrigin = request.getHeaders().getOrigin();
	// 从配置类CorsConfiguration获取允许的域名
	String allowOrigin = checkOrigin(config, requestOrigin);
	HttpHeaders responseHeaders = response.getHeaders();

	if (allowOrigin == null) {
		logger.debug("Reject: '" + requestOrigin + "' origin is not allowed");
		rejectRequest(response);
		return false;
	}

	// 获取请求的请求方法
	HttpMethod requestMethod = getMethodToUse(request, preFlightRequest);
	// 从配置类CorsConfiguration获取允许的请求方法
	List<HttpMethod> allowMethods = checkMethods(config, requestMethod);
	if (allowMethods == null) {
		logger.debug("Reject: HTTP '" + requestMethod + "' is not allowed");
		rejectRequest(response);
		return false;
	}

	// 获取请求头
	List<String> requestHeaders = getHeadersToUse(request, preFlightRequest);
	// 从配置类CorsConfiguration获取允许的请求头 
	List<String> allowHeaders = checkHeaders(config, requestHeaders);
	if (preFlightRequest && allowHeaders == null) {
		logger.debug("Reject: headers '" + requestHeaders + "' are not allowed");
		rejectRequest(response);
		return false;
	}
	// 设置响应允许域名
	responseHeaders.setAccessControlAllowOrigin(allowOrigin);

	// 如果是预检请求,设置允许请求方法
	if (preFlightRequest) {
		responseHeaders.setAccessControlAllowMethods(allowMethods);
	}

	// 设置允许请求头
	if (preFlightRequest && !allowHeaders.isEmpty()) {
		responseHeaders.setAccessControlAllowHeaders(allowHeaders);
	}

	// 设置允许响应头
	if (!CollectionUtils.isEmpty(config.getExposedHeaders())) {
		responseHeaders.setAccessControlExposeHeaders(config.getExposedHeaders());
	}
	
	// 设置是否允许请求携带cookie
	if (Boolean.TRUE.equals(config.getAllowCredentials())) {
		responseHeaders.setAccessControlAllowCredentials(true);
	}
	
	// 设置预检到期时间
	if (preFlightRequest && config.getMaxAge() != null) {
		responseHeaders.setAccessControlMaxAge(config.getMaxAge());
	}

	response.flush();
	return true;
}
  • allowedOrigin:允许域名无论什么情况都会返回
  • 只有预检请求,才有可能设置允许请求方法,请求头,响应头
  • 是否允许携带cookie和到期时间,有则返回

CorsRegistry原理

  • CorsRegistry方式最终也是通过DefaultCorsProcessor类实现
  • 与CorsFilter不同的地方
    • CorsFilter直接将属性添加到CorsConfiguration配置类
    • CorsRegistry则是通过CorsRegistration类set到CorsConfiguration配置类

4、CorsWebFilter网关WebFlux过滤器

  • reactor下的类,非mvc
@Configuration
public class CorsConfigure {
    @Bean
    public CorsWebFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedOrigin("*");
        config.addAllowedMethod("*");
        config.addAllowedHeader("*");
        config.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        configSource.registerCorsConfiguration("/**", config);
        return new CorsWebFilter(configSource);
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2023年【G2电站锅炉司炉】考试题库及G2电站锅炉司炉考试报名

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 G2电站锅炉司炉考试题库参考答案及G2电站锅炉司炉考试试题解析是安全生产模拟考试一点通题库老师及G2电站锅炉司炉操作证已考过的学员汇总&#xff0c;相对有效帮助G2电站锅炉司炉考试报名学员顺利通过考试。 1、【多…

【Java】智慧工地系统:让建筑行业管理更简单

概述 智慧工地管理平台面向房建、能源、交通各类工地的管理者&#xff0c;通过AI视频、物联感知技术对工地场景中的施工机械、建筑材料、施工规范、施工环境监管、完善施工现场项目管控。实现项目管控、特种设备管理、绿色施工、工地巡检等业务功能&#xff0c;沉淀工地监管数…

IDEA报错处理

问题1 IDEA 新建 Maven 项目没有文件结构 pom 文件为空 将JDK换成1.8后解决。 网络说法&#xff1a;别用 java18&#xff0c;换成 java17 或者 java1.8 都可以&#xff0c;因为 java18 不是 LTS 版本&#xff0c;有着各种各样的问题。。

Web(8)sqlmap工具使用

Sqlmap工具的使用&#xff1a; 首先配置Python环境变量 为什么需要环境变量&#xff1f; 感觉是在cmd中添加了一个快捷方式&#xff0c;使得可以认识 比如path&#xff0c;是告诉系统&#xff0c;当要求系统运行的程序没有告诉程序所在完整路径时&#xff0c;系统除了在当前…

C#浅拷贝和深拷贝数据

目录 一、浅拷贝 二、深拷贝 一、浅拷贝 就是把原来的数据&#xff0c;复制一份&#xff0c;但是2份数据是共享地址的&#xff0c;修改第一份数据或者修改第二份数据&#xff0c;都会一起改变&#xff0c;这可能不是我们程序中需要的场景。 下面我们演示一下&#xff0c;首…

时序预测 | Python实现GRU电力需求预测

时序预测 | Python实现GRU电力需求预测 目录 时序预测 | Python实现GRU电力需求预测预测效果基本描述程序设计参考资料预测效果 基本描述 该数据集因其每小时的用电量数据以及 TSO 对消耗和定价的相应预测而值得注意,从而可以将预期预测与当前最先进的行业预测进行比较。使用该…

MySql的增、删、改、查(MySql数据库学习——五)

增&#xff08;数据添加/插入数据&#xff09; 使用 INSERT INTO SQL 语句来插入数据。我们可以通过 mysql> 命令提示窗口中向数据表中插入数据&#xff0c;或者 通过PHP 脚本来插入数据。 sql语句&#xff1a; INSERT INTO table_name ( field1, field2,...fieldN ) …

暂退法(丢弃法)

在深度学习中&#xff0c;丢弃法&#xff08;Dropout&#xff09;是一种常用的正则化技术&#xff0c;旨在减少模型的过拟合现象&#xff0c;可能会比之前的权重衰减(Weight Decay)效果更好。通过在训练过程中随机丢弃一部分神经元&#xff0c;可以有效地减少神经网络中的参数依…

【华为数据之道学习笔记】5-4 数据入湖方式

数据入湖遵循华为信息架构&#xff0c;以逻辑数据实体为粒度入湖&#xff0c;逻辑数据实体在首次入湖时应该考虑信息的完整性。原则上&#xff0c;一个逻辑数据实体的所有属性应该一次性进湖&#xff0c;避免一个逻辑实体多次入湖&#xff0c;增加入湖工作量。 数据入湖的方式…

如何从 iPhone 上恢复已删除的照片教程分享

您是否错误地删除了 iPhone 上的错误照片&#xff1f;或者您可能已将手机恢复出厂设置&#xff0c;但现在所有照片都消失了&#xff1f;如果您现在遇到这样的情况&#xff0c;我们可以为您提供解决方案。 在本文中&#xff0c;我们将向您展示七种数据恢复方法&#xff0c;可以…

对可恢复的情况使用受检异常

在Java中&#xff0c;受检异常&#xff08;Checked Exception&#xff09;通常用于表示程序能够预期并且可能进行恢复的异常情况。这类异常是在编译时由编译器强制进行处理的&#xff0c;使得程序员必须显式处理这些异常&#xff0c;或者在方法签名中使用 throws 关键字声明。 …

Zoho Mail企业邮箱:6大高效使用技巧

本期小Z就带您了解下&#xff0c;Zoho Mail非常实用的几个小技巧&#xff0c;帮助您进一步提升工作效率。 01/轻松从其他邮箱迁移到Zoho Mail 每天我们都会收到很多垃圾邮件、网络钓鱼、或者不需要的促销邮件等等&#xff0c;筛选出这些邮件耗时耗力&#xff0c;这个时候寻找…

借着期末作业,写一个JavaWeb项目

合集传送门 要求 学生成绩管理系统设计与实现 设计一个学生成绩管理系统。根据以下功能&#xff0c;分析使用的逻辑结构和存储结构。并设计菜单&#xff0c;显示相应结果。 &#xff08;1&#xff09;录入功能&#xff1a;能够录入学生成绩&#xff08;包括&#xff1a;学号…

面向对象方法分析之 各种图

没想到研究生了还要跟这些奇奇怪怪的图打交道&#xff08;不是&#xff09; 以下是面向对象分析中常用到的图。 静态视图 静态视图对应用领域中的概念以及与系统实现有关的内部概念建模&#xff0c;主要由类以及类之间的相互关系组成&#xff0c;在静态视图中不描述依赖于时…

《opencv实用探索·二十》点追踪技术

前言&#xff1a; 在学习点追踪技术前需要先了解下光流发追踪目标&#xff0c;可以看上一章内容&#xff1a;光流法检测运动目标 如果以光流的方式追踪目标&#xff0c;基本上我们可以通过goodFeaturesToTrack函数计算一系列特征点&#xff0c;然后通过Lucas-Kanade算法进行一…

vue3 setup语法糖写法基本教程

前言 官网地址&#xff1a;Vue.js - 渐进式 JavaScript 框架 | Vue.js (vuejs.org)下面只讲Vue3与Vue2有差异的地方&#xff0c;一些相同的地方我会忽略或者一笔带过与Vue3一同出来的还有Vite&#xff0c;但是现在不使用它&#xff0c;等以后会有单独的教程使用。目前仍旧使用v…

XXE漏洞 [NCTF2019]Fake XML cookbook1

打开题目 查看源代码 发现我们post传入的数据都被放到了doLogin.php下面 访问一下看看 提示加载外部xml实体 bp抓包一下看看 得到flag 或者这样 但是很明显这样是不行的&#xff0c;因为资源是在admin上&#xff0c;也就是用户名那里 PHP引用外部实体&#xff0c;常见的利用…

Explain工具-SQL性能优化

文章目录 SQL性能优化的目标Explain中type效率级别&#xff08;重要&#xff09;注意 Explain覆盖索引ExplainindexExplainfilesortExplainfilesort创建 idx_bd(b,d) SQL性能优化的目标 达到 range 级别 Explain中type效率级别&#xff08;重要&#xff09; 显示的是单位查询…

Ubuntu系统入门指南:基础操作和使用

Ubuntu系统的基础操作和使用 一、引言二、安装Ubuntu系统三、Ubuntu系统的基础操作3.1、界面介绍3.2、应用程序的安装和卸载3.3、文件管理3.4、系统设置 四、Ubuntu系统的日常使用4.1、使用软件中心4.2、浏览器的使用和网络连接设置4.3、邮件客户端的配置和使用4.4、文件备份和…

【上海大学数字逻辑实验报告】七、中规模元件及综合设计

一、实验目的 掌握中规模时序元件的测试。学会在Quartus II上设计序列发生器。 二、实验原理 74LS161是四位可预置数二进制加计数器&#xff0c;采用16引脚双列直插式封装的中规模集成电路&#xff0c;其外形如下图所示&#xff1a; 其各引脚功能为&#xff1a; 异步复位输…