Spring Boot3中跨域访问的解决方案

186 阅读7分钟

跨域问题在前后端分离的Web项目开发中,是一个较为常见的问题。下面介绍在Spring Boot3的开发中,如何解决跨域访问的问题。

什么是“跨域”问题

如果客户端浏览器从一个源(域名、协议、端口)直接访问另一个源(域名、协议、端口)的资源时,就会被阻断而不允许访问,这就产生了所谓的“跨域”访问的问题。由于安全策略的限制,当浏览器发起这样一个跨域请求时,会被浏览器拦截,并阻止数据的传输。

以下常见的场景都是跨域访问:

  • 不同域名:

    页面的域名是 xxx.com, 而页面应用中请求的资源的域名为 yyy.com,这将产生跨域问题;

  • 不同协议:

    当请求的资源的协议与页面协议不一致时,例如:页面使用https协议,而访问资源使用http协议,或页面使用http协议,而访问资源使用https协议,都会引发跨域问题。

  • 不同端口

    如果请求的资源的端口与页面的端口不同,也会引发跨域问题,如,页面使用 xxx.com:8080,而请求资源是 xxx.com:8090 。

跨域访问限制的影响

跨域请求会受到浏览器同源策略的限制,导致请求被拒绝,难以获取到需要的数据。这对前后端分离的项目来说是非常不利的,因为客户端和服务端是两个不同的服务,客户端通过浏览器向服务端发送请求获取数据,这就会涉及到跨域问题。如果无法解决跨域问题,就会导致项目无法正常运行。

例如,前端页面[(http://127.0.0.1:8888)访问后端应用(http://localhost:8090/hello),浏览器中报错信息如下:

Access to XMLHttpRequest at 'http://localhost:8090/hello' from origin 'http://127.0.0.1:8888' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

这说明后端应用没有配置跨域访问

Spring Boot3中的跨域解决方案

Spring Boot对跨域请求的支持可以通过两种方式来配置CORS的方案来实现。通过使用CORS,开发人员可以控制哪些外部网页可以访问他们的资源,从而提高应用程序的安全性。

什么是CORS ? CORS的全称是“跨源资源共享”,是一种Web应用程序的安全机制,用于控制不同源的资源之间的交互。通过该机制,浏览器能够限制哪些外部网页可以访问来自不同源的资源。当一个网页请求另一个网页上的资源时,浏览器会检查请求是否符合CORS规范,以确定是否允许该请求。

工作原理是: 当浏览器发送一个跨域请求时,它会附加一些额外的头部信息到请求中,这些头部信息包含了关于请求的来源和目的的信息。服务器可以检查这些头部信息并决定是否允许该请求。如果服务器允许请求,它会返回一个响应,其中包含一个名为 “Access-Control-Allow-Origin”的头部信息,该信息指定了哪些源可以访问该资源。浏览器会检查返回的这个“Access-Control-Allow-Origin”头部信息,以确定是否允许该跨域请求。

Spring Boot对跨域请求的支持的配置方法:

在后端应用中,可以通过注解配置全局配置,和过滤器配置的方式均能实现前端对跨域请求的支持。

配置方法分别如下:

1. 注解配置

可以使用@CrossOrigin注解来启用CORS。例如,在需要支持跨域请求的方法上添加@CrossOrigin注解,并配置好origins和maxAge等参数。

@CrossOrigin是Spring Framework中的一个注解,用于处理跨域请求。当一个前端页面尝试从不同的源(域名、协议,或端口号)请求数据时,浏览器的同源策略会阻止这种请求,以防止潜在的安全问题。然而,在服务器的应用程序中可以使用@CrossOrigin注解,来允许前端的跨域请求,如下所示:

import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
 
    @CrossOrigin(value = "http://127.0.0.1:5500",allowedHeaders = "*",allowCredentials = "true")
    @GetMapping("/hello")
    public String hello(){
        return "Hello";
    }
}

@CrossOrigin注解的属性说明:

  • value和origins属性: 都用于指定允许访问的源的列表,即允许跨域请求的域名或IP地址(可以是域名、IP 或其他标识符)。多个源可以使用逗号分隔。但要注意,如果同时使用value和origins属性,它们的值必须一致,否则会在启动时出现异常。
  • methods: 允许的 HTTP 方法列表。例如,只允许 GET 请求。
  • allowedHeaders: 允许的请求头列表。默认情况下,允许所有请求头。
  • allowCredentials:是否允许配置;值为true、false的字符串
  • maxAge: 预检请求的缓存时间(以秒为单位)。默认是 86400 秒(24小时)。这些属性可以根据需要进行组合和配置。

2. 全局配置

可以通过实现WebMvcConfigurer接口并注册一个WebMvcConfigurer bean来配置CORS的全局设置。在实现类中覆盖addCorsMappings方法,通过CorsRegistry对象添加映射规则。默认情况下,所有方法都支持跨域,并且GET、POST和HEAD请求是被允许的。如果需要自定义,可以配置CorsRegistry对象来指定允许的域名、端口和请求方法等。

addCorsMappings 是 Spring Boot 中用于配置跨域请求的方法。它允许你指定哪些路径的请求需要进行跨域处理,以及如何处理这些请求。

在 addCorsMappings 方法中,你可以使用 addMapping 方法来指定需要跨域处理的请求路径。

例如:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**") // 允许所有请求路径跨域访问
                .allowCredentials(true) // 是否携带Cookie,默认false
                .allowedHeaders("*") // 允许的请求头类型
                .maxAge(3600)  // 预检请求的缓存时间(单位:秒)
                .allowedMethods("*") // 允许的请求方法类型
                .allowedOrigins("http://127.0.0.1:5500"); // 允许哪些域名进行跨域访问
    }
}   

3. 过滤器配置

在Spirng Boot3中,CorsFilter用于处理跨域请求,它是一个过滤器,用于在Spring应用程序中启用CORS支持。Spring Boot 提供了 WebMvcConfigurer 接口来帮助我们定制化Web应用的配置,包括CORS(Cross-Origin Resource Sharing)设置。可以通过CorsFilter bean来配置CORS的过滤器。这种方式可以更加灵活地控制CORS的配置,例如只允许特定的域名进行跨域访问等。

以下是一个简单的配置类示例:CprsConfig.java


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 {
    @Bean
    public CorsFilter corsFilter(){
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedHeader("*");   // 设置访问源请求头
        config.addAllowedMethod("*");   // 设置访问源请求方法
        config.addAllowedOriginPattern("*");    // 设置访问源地址,(*)表示匹配所有。
        config.setAllowCredentials(true);
        // config.addAllowedOrigin("http://127.0.0.1:8888");
        // config.setMaxAge(1800L);    // 有效期 1800秒,设置预检请求(OPTIONS请求)的缓存时间,1800秒意味着客户端可以缓存这个设置1800秒
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

        source.registerCorsConfiguration("/**",config);

        return  new CorsFilter(source);
    }
}

通过配置CorsFilter过滤器解决跨域问题的方式,是比较常用的一种解决方式。

注意事项:

  1. 当开启withCredentials:true的时候但没配置allowCredentials为true时会提示:

Access to XMLHttpRequest at 'http://localhost:8090/hello' from origin 'http://127.0.0.1:8888' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

  1. 当在后端配置了allowCredentials(true),那么就不能配置 allowedOrigins("*"),而是必须要指定来源,否则会报以下错误:

jakarta.servlet.ServletException: Request processing failed: java.lang.IllegalArgumentException: When allowCredentials is true, allowedOrigins cannot contain the special value "*" since that cannot be set on the "Access-Control-Allow-Origin" response header. To allow credentials to a set of origins, list them explicitly or consider using "allowedOriginPatterns" instead.