Web端系统开发解决跨域问题

一、同源策略简介

1.1、什么是源

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

1.2、什么是同源

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

1.3、是否是同源的判断

判断两个URL是否同源,需要比较它们的协议、域名和端口号是否完全相同。以下是一些示例:
http://www.a.com/dir/page.htmlhttp://www.a.com/test/index.html 同源(协议、域名、端口号均相同)
http://www.child.a.com/test/index.htmlhttp://www.a.com/test/index.html 不同源(域名不同)
https://www.a.com/test/index.htmlhttp://www.a.com/test/index.html 不同源(协议不同)
http://www.a.com:8080/test/index.htmlhttp://www.a.com/test/index.html 不同源(端口号不同)
1.4、哪些操作不受同源策略限制
尽管同源策略严格限制了跨源的资源访问,但以下操作通常不受其限制:
页面中的链接:用户点击链接跳转到其他网站时,不受同源策略限制。
重定向:页面重定向到另一个URL时,也不受同源策略限制。
表单提交:表单数据可以提交到与当前页面不同源的服务器。
跨域资源的嵌入:如<script src="...">、<img>、<link>、<iframe>等标签可以嵌入来自不同源的资源,但脚本不能通过DOM API访问这些资源的内容。

1.5、跨域的概念

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

1.6、跨域解决方案

为了解决跨域问题,可以采取以下几种方法:
JSONP:一种利用<script>标签不受同源策略限制的特性实现的跨域数据交换方式。但JSONP只支持GET请求,且存在安全风险。
CORS(Cross-Origin Resource Sharing):现代浏览器支持的跨域资源共享标准。通过服务器设置特定的HTTP响应头(如Access-Control-Allow-Origin)来允许或拒绝跨域请求。CORS是目前最常用且最安全的跨域解决方案。
代理服务器:将跨域请求转发到同源的代理服务器上,再由代理服务器向目标服务器发起请求,最后将响应返回给客户端。这种方法可以绕过浏览器的同源策略限制,但需要在服务器端进行额外的配置。
降域:通过修改document.domain属性(仅适用于子域之间的跨域),使不同子域的页面能够相互访问。但这种方法存在安全风险,且应用场景有限。
其他技术:如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)的缓存时间,单位为秒。预检请求是浏览器在发送实际请求之前发送的一种请求,用于检查服务器是否允许跨域请求。设置这个值可以减少预检请求的频率,提高性能。

类级别使用
@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!";  
    }  
}

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

方法级别使用
@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!";  
    }  
}

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

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

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

2. 配置全局CORS

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

方式一:通过CorsFilterBean配置CORS。

步骤细节:

定义CORS配置:首先,我们定义一个私有方法(如buildConfig),用于构建并返回一个CorsConfiguration对象。在这个对象中,我们设置了允许的源(allowedOrigins)、允许的HTTP方法(allowedMethods)、允许的头部(allowedHeaders)以及是否允许发送Cookie(setAllowCredentials)。

注册CORS配置:接着,我们创建一个CorsFilter的Bean(如corsFilter方法)。在这个方法中,我们首先创建一个UrlBasedCorsConfigurationSource对象,这个对象用于将特定的CORS配置与特定的URL模式关联起来。然后,我们使用registerCorsConfiguration方法将之前构建的CORS配置应用到所有URL模式(/**)上。

整合到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);
    }
}
方式二:通过实现WebMvcConfigurer接口配置CORS

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

步骤细节:

实现WebMvcConfigurer接口:首先,我们创建一个配置类(如GlobalCorsConfig),并让它实现WebMvcConfigurer接口。

重写addCorsMappings方法:然后,我们在这个配置类中重写addCorsMappings方法。在这个方法中,我们使用CorsRegistry对象来定义CORS策略。我们通过调用addMapping方法来指定哪些URL模式应该应用这些策略,并通过链式调用allowedOrigins、allowedMethods、allowedHeaders、allowCredentials和maxAge等方法来设置具体的CORS规则。

整合到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);
    }
    
}

总结

使用@CrossOrigin注解可以快速地为单个控制器或方法启用CORS,适用于简单的CORS需求。
全局CORS配置,例如通过实现WebMvcConfigurer接口并重写addCorsMappings方法来实现,适用于需要为整个应用设置统一CORS策略的场景。这种方式更加灵活,能够更好地满足复杂的CORS需求。

posted @ 2024-10-29 16:10  噗噗噗i丶  阅读(20)  评论(0编辑  收藏  举报