Web端系统开发解决跨域问题——以Java SpringBoot框架配置Cors为例

本文涉及的产品
.cn 域名,1个 12个月
简介: 在Web安全上下文中,源(Origin)是指一个URL的协议、域名和端口号的组合。这三个部分共同定义了资源的来源,浏览器会根据这些信息来判断两个资源是否属于同一源。例如,https://www.example.com:443和http://www.example.com虽然域名相同,但由于协议和端口号不同,它们被视为不同的源。同源(Same-Origin)是指两个URL的协议、域名和端口号完全相同。只有当这些条件都满足时,浏览器才认为这两个资源来自同一源,从而允许它们之间的交互操作。

在Web端系统开发中,跨域问题是一个常见且需要妥善处理的问题。跨域(Cross-Origin)主要指的是在浏览器中,当前网页的域名、协议、端口与请求资源的域名、协议、端口不一致时,就发生了跨域。由于浏览器出于安全考虑,限制了不同域之间的资源访问,这主要是为了防止恶意网站利用用户的身份进行一些危险的操作,从而保护用户的隐私和安全。

一、同源策略简介

1.1、什么是源

在Web安全上下文中,源(Origin)是指一个URL的协议、域名和端口号的组合。这三个部分共同定义了资源的来源,浏览器会根据这些信息来判断两个资源是否属于同一源。例如,https://www.example.com:443http://www.example.com虽然域名相同,但由于协议和端口号不同,它们被视为不同的源。

1.2、什么是同源

同源(Same-Origin)是指两个URL的协议、域名和端口号完全相同。只有当这些条件都满足时,浏览器才认为这两个资源来自同一源,从而允许它们之间的交互操作。同源策略是浏览器安全策略的一部分,用于限制不同源之间的资源访问,以防止恶意网站窃取或篡改用户数据。

1.3、是否是同源的判断

判断两个URL是否同源,需要比较它们的协议、域名和端口号是否完全相同。以下是一些示例:

1.4、哪些操作不受同源策略限制

尽管同源策略严格限制了跨源的资源访问,但以下操作通常不受其限制:

  1. 页面中的链接:用户点击链接跳转到其他网站时,不受同源策略限制。
  2. 重定向:页面重定向到另一个URL时,也不受同源策略限制。
  3. 表单提交:表单数据可以提交到与当前页面不同源的服务器。
  4. 跨域资源的嵌入:如<script src="...">、<img>、<link>、<iframe>等标签可以嵌入来自不同源的资源,但脚本不能通过DOM API访问这些资源的内容。

1.5、跨域的概念

跨域(Cross-Origin)是指浏览器尝试访问或操作与当前页面不同源的资源。由于同源策略的限制,跨域请求通常会被浏览器阻止,除非服务器明确允许(如通过CORS头部)。跨域问题在Web开发中非常常见,特别是在需要调用第三方API或在不同子域之间共享资源时。

1.6、跨域解决方案

为了解决跨域问题,可以采取以下几种方法:

  1. JSONP:一种利用<script>标签不受同源策略限制的特性实现的跨域数据交换方式。但JSONP只支持GET请求,且存在安全风险。
  2. CORS(Cross-Origin Resource Sharing):现代浏览器支持的跨域资源共享标准。通过服务器设置特定的HTTP响应头(如Access-Control-Allow-Origin)来允许或拒绝跨域请求。CORS是目前最常用且最安全的跨域解决方案。
  3. 代理服务器:将跨域请求转发到同源的代理服务器上,再由代理服务器向目标服务器发起请求,最后将响应返回给客户端。这种方法可以绕过浏览器的同源策略限制,但需要在服务器端进行额外的配置。
  4. 降域:通过修改document.domain属性(仅适用于子域之间的跨域),使不同子域的页面能够相互访问。但这种方法存在安全风险,且应用场景有限。
  5. 其他技术:如window.postMessage、WebSocket等也可以用于实现跨域通信,但它们的使用场景和限制条件各不相同。

二、CORS 简介

CORS(Cross-Origin Resource Sharing,跨源资源共享)是由W3C提出的一种机制,旨在解决浏览器同源策略(Same-Origin Policy)的限制,允许网页从不同的源(协议+域名+端口)加载资源。CORS通过服务器设置特定的HTTP头部信息,来告诉浏览器哪些跨域请求是被允许的,从而实现了安全的跨域通信。

2.1、CORS 的核心思想

  • 不破坏既有规则:CORS在保持浏览器同源策略的基础上,提供了一种机制来允许跨域请求。
  • 服务器控制:CORS的实现完全依赖于服务器端的配置,服务器通过发送特定的HTTP头部来告知浏览器哪些跨域请求是被允许的。

2.2、CORS 请求分类

CORS将跨域请求分为两类:简单请求(Simple Requests)和非简单请求(Preflighted Requests)。

1. 简单请求

简单请求是指那些满足以下条件的HTTP请求:

  • 请求方法只能是GET、HEAD或POST。
  • 对于POST方法,Content-Type的值只能是application/x-www-form-urlencoded、multipart/form-data或text/plain。
  • 请求中的HTTP头信息不能包含除上述简单请求允许字段外的其他自定义字段。

简单请求在发送时,浏览器会自动在请求头中添加Origin字段,表明请求的来源。服务器根据Origin字段的值判断是否允许该跨域请求。如果允许,服务器会在响应头中添加Access-Control-Allow-Origin字段,并可能包含其他CORS相关的字段,如Access-Control-Allow-Credentials、Access-Control-Expose-Headers等。

2. 非简单请求

对于不满足简单请求条件的跨域请求,浏览器会先发送一个OPTIONS请求作为预检请求(Preflight Request),以询问服务器是否允许该跨域请求。预检请求会包含以下CORS相关的HTTP头信息:

  • Origin:表明请求的来源。
  • Access-Control-Request-Method:实际请求将使用的HTTP方法。
  • Access-Control-Request-Headers:实际请求将携带的自定义HTTP头信息字段。

服务器收到预检请求后,会检查Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段,以决定是否允许该跨域请求。如果允许,服务器会在响应头中添加以下CORS相关的字段:

  • Access-Control-Allow-Origin:表明允许哪个源的请求。
  • Access-Control-Allow-Methods:表明允许哪些HTTP方法。
  • Access-Control-Allow-Headers:表明允许哪些自定义HTTP头信息字段。
  • Access-Control-Allow-Credentials:表明是否允许发送Cookie。
  • Access-Control-Max-Age:表明预检请求的有效期(单位为秒),在有效期内,浏览器不会重复发送预检请求。

当预检请求通过后,浏览器才会发送实际的跨域请求。

三、SpringBoot配置Cors解决跨域问题

在Spring Boot中处理跨域资源共享(CORS)问题,可以通过几种方式来实现。这里主要介绍两种常见的方法:使用@CrossOrigin注解和配置全局CORS。

1. 使用@CrossOrigin注解

在Spring Boot中@CrossOrigin注解是一个非常有用的工具,用于处理跨域资源共享(CORS)问题。这个注解可以被放置在类级别或方法级别,以控制哪些跨域请求被允许。它提供了灵活的CORS支持,允许你指定哪些源(origins)、HTTP方法、头部(headers)和是否允许发送Cookie等。

下面我将详细解释@CrossOrigin注解及其各个属性的含义,并展示如何将它们应用于类和方法。

属性说明

  • origins:允许访问该资源的源列表。可以使用通配符*来允许所有源,或者使用具体的URL列表。
  • methods:允许的HTTP方法列表,如GET、POST、PUT、DELETE等。默认允许所有方法。
  • allowedHeaders:允许的HTTP头部列表。可以使用*来允许所有头部。
  • exposedHeaders:浏览器可以访问的响应头部列表。这些头部通常由后端设置,但浏览器出于安全考虑可能默认不暴露给前端。
  • allowCredentials:是否允许发送Cookie。默认情况下,CORS请求不会发送Cookie。当设置为true时,允许发送Cookie,但此时origins不能设置为*,必须明确指定具体的源。
  • maxAge:预检请求(preflight request)的缓存时间,单位为秒。预检请求是浏览器在发送实际请求之前发送的一种请求,用于检查服务器是否允许跨域请求。设置这个值可以减少预检请求的频率,提高性能。

类级别使用

当@CrossOrigin注解被放置在类上时,它会影响该类中所有方法的CORS设置。

@RestController  
@CrossOrigin(origins = "http://example.com", maxAge = 3600)  
public class MyController {  
  
    @GetMapping("/greeting")  
    public String greeting() {  
        return "Hello, World!";  
    }  
  
    // 这个方法也会继承类级别的CORS设置  
    @PostMapping("/postGreeting")  
    public String postGreeting() {  
        return "Hello, POST World!";  
    }  
}

image.gif

方法级别使用

@RestController  
public class MyController {  
  
    @CrossOrigin(origins = "http://specific.com", allowCredentials = "true")  
    @GetMapping("/secureGreeting")  
    public String secureGreeting() {  
        return "Secure Hello, World!";  
    }  
  
    // 这个方法不会继承任何CORS设置,因为它没有@CrossOrigin注解  
    @GetMapping("/noCorsGreeting")  
    public String noCorsGreeting() {  
        return "Hello, No CORS!";  
    }  
}

image.gif

你也可以在方法级别使用@CrossOrigin注解,以覆盖类级别的设置或仅为该方法提供CORS支持。

在上面的例子中,secureGreeting方法通过@CrossOrigin注解明确指定了允许来自http://specific.com的跨域请求,并允许发送Cookie。而noCorsGreeting方法则没有CORS支持,因此它不能响应来自不同源的跨域请求。

通过这种方式,你可以非常灵活地控制你的Spring Boot应用的CORS策略,以满足不同的安全和性能需求。

2. 配置全局CORS

如果你希望为你的整个应用设置统一的CORS策略,而不是在每个控制器或方法上单独设置,那么全局CORS配置是一个更好的选择。Spring Boot允许你通过添加一个CORS配置类来实现这一点。

方式一:通过CorsFilterBean配置CORS

步骤细节

  1. 定义CORS配置
    首先,我们定义一个私有方法(如buildConfig),用于构建并返回一个CorsConfiguration对象。在这个对象中,我们设置了允许的源(allowedOrigins)、允许的HTTP方法(allowedMethods)、允许的头部(allowedHeaders)以及是否允许发送Cookie(setAllowCredentials)。
  2. 注册CORS配置
    接着,我们创建一个CorsFilter的Bean(如corsFilter方法)。在这个方法中,我们首先创建一个UrlBasedCorsConfigurationSource对象,这个对象用于将特定的CORS配置与特定的URL模式关联起来。然后,我们使用registerCorsConfiguration方法将之前构建的CORS配置应用到所有URL模式(/**)上。
  3. 整合到Spring Boot应用:
    最后,由于CorsFilter的Bean是在一个带有@Configuration注解的类中定义的,因此Spring Boot会自动检测到它,并将其注册到应用的上下文中。这样,每当有HTTP请求到达时,CorsFilter就会根据配置的CORS策略来检查并处理这些请求。
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;
@Configuration
public class CorsConfig {
    private CorsConfiguration buildConfig() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*"); // 允许任何域名使用
        corsConfiguration.addAllowedHeader("*"); // 允许任何头
        corsConfiguration.addAllowedMethod("*"); // 允许任何方法(post、get等)
        corsConfiguration.setAllowCredentials(true);
        return corsConfiguration;
    }
    
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", buildConfig()); // 对接口配置跨域设置
        return new CorsFilter(source);
    }
}

image.gif

方式二:通过实现WebMvcConfigurer接口配置CORS

我们通过实现WebMvcConfigurer接口并重写addCorsMappings方法来配置CORS。这种方式利用了Spring MVC的自动配置机制,使得CORS配置更加简洁和直观。

步骤细节:

  1. 实现WebMvcConfigurer接口:
    首先,我们创建一个配置类(如GlobalCorsConfig),并让它实现WebMvcConfigurer接口。
  2. 重写addCorsMappings方法:
    然后,我们在这个配置类中重写addCorsMappings方法。在这个方法中,我们使用CorsRegistry对象来定义CORS策略。我们通过调用addMapping方法来指定哪些URL模式应该应用这些策略,并通过链式调用allowedOrigins、allowedMethods、allowedHeaders、allowCredentials和maxAge等方法来设置具体的CORS规则。
  3. 整合到Spring Boot应用:
    由于这个配置类被@Configuration注解标记,Spring Boot会自动检测到它,并在启动时调用addCorsMappings方法来配置CORS。
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration //加配置注解可以扫描到
public class WebConfig implements WebMvcConfigurer{
    
    //跨域请求配置
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        WebMvcConfigurer.super.addCorsMappings(registry);
        registry.addMapping("/**")// 对接口配置跨域设置
                .allowedHeaders("*")// 允许任何头
                .allowedMethods("POST","GET")// 允许方法(post、get等)
                .allowedOrigins("*")// 允许任何域名使用
                .allowCredentials(true);
    }
    
}

image.gif

总结

  • 使用@CrossOrigin注解可以快速地为单个控制器或方法启用CORS,适用于简单的CORS需求。
  • 全局CORS配置,例如通过实现WebMvcConfigurer接口并重写addCorsMappings方法来实现,适用于需要为整个应用设置统一CORS策略的场景。这种方式更加灵活,能够更好地满足复杂的CORS需求。
相关文章
|
6天前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
22 4
|
8天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,包括版本兼容性、安全性、性能调优等方面。
62 1
|
3天前
|
Java API 数据库
Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐
本文通过在线图书管理系统案例,详细介绍如何使用Spring Boot构建RESTful API。从项目基础环境搭建、实体类与数据访问层定义,到业务逻辑实现和控制器编写,逐步展示了Spring Boot的简洁配置和强大功能。最后,通过Postman测试API,并介绍了如何添加安全性和异常处理,确保API的稳定性和安全性。
10 0
|
2天前
|
druid Java Maven
|
9天前
|
监控 Java Maven
springboot学习二:springboot 初创建 web 项目、修改banner、热部署插件、切换运行环境、springboot参数配置,打包项目并测试成功
这篇文章介绍了如何快速创建Spring Boot项目,包括项目的初始化、结构、打包部署、修改启动Banner、热部署、环境切换和参数配置等基础操作。
44 0
|
4天前
|
安全 Java UED
Java中的多线程编程:从基础到实践
本文深入探讨了Java中的多线程编程,包括线程的创建、生命周期管理以及同步机制。通过实例展示了如何使用Thread类和Runnable接口来创建线程,讨论了线程安全问题及解决策略,如使用synchronized关键字和ReentrantLock类。文章还涵盖了线程间通信的方式,包括wait()、notify()和notifyAll()方法,以及如何避免死锁。此外,还介绍了高级并发工具如CountDownLatch和CyclicBarrier的使用方法。通过综合运用这些技术,可以有效提高多线程程序的性能和可靠性。
|
4天前
|
缓存 Java UED
Java中的多线程编程:从基础到实践
【10月更文挑战第13天】 Java作为一门跨平台的编程语言,其强大的多线程能力一直是其核心优势之一。本文将从最基础的概念讲起,逐步深入探讨Java多线程的实现方式及其应用场景,通过实例讲解帮助读者更好地理解和应用这一技术。
20 3
|
8天前
|
Java 调度 UED
深入理解Java中的多线程与并发机制
本文将详细探讨Java中多线程的概念、实现方式及并发机制,包括线程的生命周期、同步与锁机制以及高级并发工具。通过实例代码演示,帮助读者理解如何在Java中有效地处理多线程和并发问题,提高程序的性能和响应能力。
|
6天前
|
缓存 安全 Java
使用 Java 内存模型解决多线程中的数据竞争问题
【10月更文挑战第11天】在 Java 多线程编程中,数据竞争是一个常见问题。通过使用 `synchronized` 关键字、`volatile` 关键字、原子类、显式锁、避免共享可变数据、合理设计数据结构、遵循线程安全原则和使用线程池等方法,可以有效解决数据竞争问题,确保程序的正确性和稳定性。
13 2
|
7天前
|
存储 安全 Java
Java-如何保证线程安全?
【10月更文挑战第10天】