欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 产业 > SpringBoot 跨域请求处理全攻略:从原理到实践

SpringBoot 跨域请求处理全攻略:从原理到实践

2024/10/25 12:16:33 来源:https://blog.csdn.net/weixin_68020300/article/details/140514936  浏览:    关键词:SpringBoot 跨域请求处理全攻略:从原理到实践

文章目录

  • SpringBoot 如何处理跨域请求?你能说出几种方法?
    • 跨域请求概述
    • 跨域解决方案
      • 1. 使用@CrossOrigin注解
      • 2. 使用WebMvcConfigurer配置类
      • 3. 使用过滤器(Filter)
      • 4. 使用Spring Security处理CORS
      • 5.使用Spring Cloud Gateway处理CORS
    • 补充
      • 1. 预检请求(Preflight Requests)
      • 2. 其他注意事项
    • 总结
    • 思考
      • 1. 如何区分简单请求和复杂请求?
      • 2. 在实际项目中,如何选择合适的跨域请求解决方案?
      • 3. 如何处理凭证(Credentials)在跨域请求中的应用?

SpringBoot 如何处理跨域请求?你能说出几种方法?

在现代的Web开发中,跨域请求(Cross-Origin Resource Sharing,CORS)是一个常见的挑战。随着前后端分离架构的流行,前端应用通常运行在一个与后端 API 不同的域名或端口上,这就导致了浏览器的同源策略(Same-Origin Policy)的限制,从而出现了跨域请求问题。

Spring Boot 作为一种流行的 Java 后端框架,提供了多种处理跨域请求的方法,使得开发人员能够灵活地配置和管理跨域资源共享。本文将深入探讨几种常见的解决方案,帮助开发人员理解如何在 Spring Boot 应用中有效地处理跨域请求问题。

跨域请求概述

  1. 什么是跨域请求?
    跨域请求(Cross-Origin Request)指的是在浏览器环境下,前端代码发起的请求与当前页面的域名(或端口、协议)不同。浏览器的同源策略(Same-Origin Policy)限制了从一个源(域名、协议、端口组成的组合)加载的文档或脚本如何与来自另一个源的资源进行交互。

    具体来说,如果一个页面加载自 http://domain1.com,则它的同源策略默认限制了对 http://domain2.com 发起的请求。
    跨域问题是浏览器的一种安全策略,访问需要遵循同源策略:

URL说明是否允许通信
http://www.a.com/a.js http://www.a.com/b.js同一域名下允许
http://www.a.com/lab/a.js http://www.a.com/script/b.js 同一域名下不同文件夹允许
http://www.a.com:8000/a.js http://www.a.com/b.js同一域名,不同端口不允许
http://www.a.com/a.js https://www.a.com/b.js同一域名,不同协议不允许
http://www.a.com/a.js http://192.168.110.11/b.js域名和域名对应ip不允许
http://www.a.com/a.js http://script.a.com/b.js主域相同,子域不同不允许
http://www.a.com/a.js http://a.com/b.js同一域名,不同二级域名(同上)不允许(cookie这种情况下也不允许访问)
http://www.cnblogs.com/a.js http://www.a.com/b.js不同域名不允许
  1. 为什么会出现跨域问题?

    跨域问题的出现主要是为了保护用户数据和用户隐私安全。如果没有同源策略的限制,恶意网站可以利用当前用户的身份在其他网站上进行操作,如发送请求、读取数据,这可能导致信息泄露或潜在的安全威胁。

  2. 可能会导致的安全风险

    • 信息泄露:允许恶意网站读取其他网站的敏感数据。
    • CSRF(跨站请求伪造)攻击:如果没有适当的防护措施,允许攻击者伪造用户的请求并在用户不知情的情况下执行操作。
    • 恶意脚本注入:通过跨域请求注入恶意脚本,影响其他域上的安全性。

跨域解决方案

1. 使用@CrossOrigin注解

Spring 框架提供了@CrossOrigin注解,可以直接在 Controller 类或方法上使用,以声明允许来自特定源的请求。例如:

@RestController
@RequestMapping("/api")
public class MyController {@CrossOrigin(origins = "http://allowed-origin.com")@GetMapping("/data")public ResponseEntity<String> getData() {// 处理逻辑}
}

优点:简单直接,适用于简单的跨域场景。

缺点:无法进行更细粒度的配置,如请求方法、请求头等。

2. 使用WebMvcConfigurer配置类

通过自定义WebMvcConfigurer配置类,可以更灵活地配置跨域请求。例如:

@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/api/**").allowedOrigins("http://allowed-origin.com").allowedMethods("GET", "POST").allowedHeaders("header1", "header2").exposedHeaders("header3").allowCredentials(true).maxAge(3600);}
}

优点:可以精确控制允许的源、方法、头部等。

缺点:需要编写额外的配置类,相对比较复杂。

3. 使用过滤器(Filter)

通过自定义过滤器来处理跨域请求,这种方法可以在请求到达 Controller 之前进行处理。例如:

@Component
public class CorsFilter implements Filter {@Overridepublic void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {HttpServletResponse response = (HttpServletResponse) res;HttpServletRequest request = (HttpServletRequest) req;response.setHeader("Access-Control-Allow-Origin", "http://allowed-origin.com");response.setHeader("Access-Control-Allow-Methods", "GET,POST");response.setHeader("Access-Control-Allow-Headers", "header1,header2");response.setHeader("Access-Control-Expose-Headers", "header3");response.setHeader("Access-Control-Allow-Credentials", "true");response.setHeader("Access-Control-Max-Age", "3600");chain.doFilter(req, res);}
}

优点:可以完全自定义跨域请求处理逻辑。

缺点:需要更多的Java编程经验,并且可能需要处理更复杂的跨域场景。
在使用Spring Security和Spring Cloud Gateway时,处理跨域请求(CORS)可以通过不同的方式实现。对于Spring Security,你可以在安全配置中添加CORS配置;而对于Spring Cloud Gateway,你可以使用全局过滤器或者特定的路由过滤器来处理CORS。

4. 使用Spring Security处理CORS

在Spring Security中,你可以在WebSecurityConfigurerAdapter的配置类中添加一个CorsConfigurationSource来指定CORS策略。但是,如果你使用的是Spring Security 5.3及以上版本,推荐使用WebSecurityCustomizer来配置CORS。

示例代码如下:

@Configuration
@EnableWebSecurity
public class SecurityConfig {@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.cors().and().csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()// ... your authorization rules here.anyRequest().authenticated();http.addFilterBefore(new JWTAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);return http.build();}@BeanCorsConfigurationSource corsConfigurationSource() {CorsConfiguration configuration = new CorsConfiguration();configuration.setAllowedOrigins(Arrays.asList("*"));configuration.setAllowedMethods(Arrays.asList("GET","POST","PUT","DELETE","OPTIONS"));configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", configuration);return source;}
}

5.使用Spring Cloud Gateway处理CORS

在Spring Cloud Gateway中,你可以在全局过滤器中添加CORS处理逻辑,或者在每个路由上单独配置CORS。这里是一个使用全局过滤器的例子:

@Configuration
public class CorsConfig {@Beanpublic GatewayFilter globalCorsFilter() {CorsConfiguration corsConfig = new CorsConfiguration();corsConfig.setAllowCredentials(true);corsConfig.addAllowedOriginPattern("*");corsConfig.addAllowedHeader(HttpHeaders.AUTHORIZATION);corsConfig.addAllowedHeader(HttpHeaders.CONTENT_TYPE);corsConfig.addAllowedMethod(HttpMethod.GET.name());corsConfig.addAllowedMethod(HttpMethod.POST.name());corsConfig.addAllowedMethod(HttpMethod.PUT.name());corsConfig.addAllowedMethod(HttpMethod.DELETE.name());corsConfig.addAllowedMethod(HttpMethod.OPTIONS.name());List<String> allowedMethods = corsConfig.getAllowedMethods();corsConfig.setExposedHeaders(allowedMethods.stream().map(method -> "X-Permitted-Cross-Domain-Policies").collect(Collectors.toList()));CorsGatewayFilterFactory corsGatewayFilterFactory = new CorsGatewayFilterFactory(corsConfig);return corsGatewayFilterFactory.apply(new CorsConfig().new CorsConfigCustomizer());}public static class CorsConfigCustomizer implements CorsGatewayFilterFactory.CorsConfigurationCustomizer {@Overridepublic void customize(CorsConfiguration corsConfiguration) {corsConfiguration.setMaxAge(3600L);}}
}

请注意,这些示例代码需要根据你的具体需求进行调整。例如,你可能需要限制允许的源,或者修改其他CORS设置。

补充

1. 预检请求(Preflight Requests)

跨域请求中,某些复杂请求(如带有自定义头部的请求、使用某些特殊方法如PUTDELETE等的请求)会触发浏览器先发送一个预检请求(OPTIONS请求)到服务器,以确定是否允许实际的请求。预检请求包含以下头部信息:

  • Origin:表明发起请求的源。
  • Access-Control-Request-Method:实际请求将使用的HTTP方法。
  • Access-Control-Request-Headers:实际请求将使用的自定义头部。

服务器需要正确响应预检请求,以确保浏览器安全地执行实际请求。
处理预检请求的方法:

  1. 使用@CrossOrigin注解处理

    	@CrossOrigin(origins = "http://allowed-origin.com", methods = {RequestMethod.GET, RequestMethod.POST}, allowedHeaders = {"header1", "header2"})@RequestMapping(value = "/api/data", method = RequestMethod.OPTIONS)public ResponseEntity<Void> preflight() {return ResponseEntity.ok().build();}
    

    Controller中定义一个处理OPTIONS请求的方法,并使用@CrossOrigin注解指定允许的源、方法和头部。

  2. 通过WebMvcConfigurer配置类处理

    @Configuration
    public class WebConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/api/**").allowedOrigins("http://allowed-origin.com").allowedMethods("GET", "POST").allowedHeaders("header1", "header2").exposedHeaders("header3").allowCredentials(true).maxAge(3600);}@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/api/**").allowedOrigins("http://allowed-origin.com").allowedMethods("GET", "POST").allowedHeaders("header1", "header2").exposedHeaders("header3").allowCredentials(true).maxAge(3600);}
    }
    

    WebMvcConfigurer配置类中,通过重写addCorsMappings方法来定义预检请求的处理方式。

2. 其他注意事项

Credentials(凭证)的处理
如果请求需要使用凭证(如使用CookieHTTP认证信息等),需要确保在跨域请求中设置Access-Control-Allow-Credentialstrue,并在客户端请求中设置withCredentialstrue

暴露自定义头部
如果需要在响应中暴露某些自定义头部供客户端访问,可以通过Access-Control-Expose-Headers头部指定。

缓存控制
可以通过Access-Control-Max-Age头部指定预检请求的缓存时间,减少重复发送预检请求的次数,提升性能。

总结

避免跨域请求问题不仅仅是简单地允许跨域请求,还需要正确处理预检请求及相关细节。通过使用@CrossOrigin注解、自定义WebMvcConfigurer配置类或过滤器来处理跨域请求,可以有效地保证跨域请求的安全性和可靠性。同时,要特别注意凭证的处理及其他相关头部信息的配置,确保跨域请求能够在安全、可控的环境下完成。

思考

1. 如何区分简单请求和复杂请求?

跨域资源共享(CORS)规范将跨域请求分为两类:简单请求和预检请求(复杂请求)。区分这两类请求主要基于以下几个因素:

简单请求:

  • 方法:请求方法必须是以下之一:GET, HEAD, POST
  • 头信息:请求头中的字段不能包含除了Accept, Accept-Language, Content-Language, Last-Event-ID, Content-Type之外的自定义头部,且Content-Type的值只能是以下几种之一:application/x-www-form-urlencoded, multipart/form-data, 或者 text/plain
  • 数据类型:如果是POST请求,发送的数据必须符合上述Content-Type的规定。

复杂请求(预检请求):

  • 如果请求不符合上述简单请求的标准,则被视为复杂请求。浏览器会先发送一个OPTIONS请求到服务器,这个请求称为预检请求,用于确认服务器是否支持跨域请求以及具体的请求方法和头部信息。

预检请求包含了实际请求的所有关键信息,包括请求方法、请求头部等,服务器通过预检请求响应告知客户端是否可以继续发送实际的请求。

2. 在实际项目中,如何选择合适的跨域请求解决方案?

选择跨域请求解决方案主要考虑以下几个方面:

  • 安全性:确保跨域访问不会导致安全漏洞,比如XSS攻击或CSRF攻击。
  • 性能:预检请求会增加额外的网络延迟,因此在设计API时应尽量避免不必要的复杂请求。
  • 功能需求:根据API的功能需求确定哪些HTTP方法和头信息是必要的,这将影响请求的类型。
  • 易用性:为开发者提供清晰的文档和示例,说明哪些资源可以被跨域访问,以及如何正确地设置请求头。

在Spring框架下,可以采用以下策略:

  • 使用Spring Security或Spring MVC的内置CORS支持,通过配置类来指定CORS策略。
  • 在Spring Cloud Gateway中,通过全局过滤器或路由级过滤器来处理CORS,这样可以更细粒度地控制跨域策略。

3. 如何处理凭证(Credentials)在跨域请求中的应用?

凭证通常指的是Cookie和HTTP认证信息(如Basic Auth),它们在跨域请求中默认是不会被发送的。如果需要发送凭证,需要在CORS策略中显式地允许:

  • 在服务器端,需要在CORS响应头中添加Access-Control-Allow-Credentials: true
  • 在客户端,当发起请求时,需要将withCredentials属性设置为true

示例代码:

服务端配置(Spring Security)

CorsConfiguration corsConfig = new CorsConfiguration();
corsConfig.setAllowCredentials(true); // 允许发送凭证

客户端调用(JavaScript/AJAX)

fetch('http://example.com/api', {method: 'GET',credentials: 'include', // 发送凭证
});

请注意,一旦允许了凭证的发送,所有跨域请求都必须遵循CORS策略,并且不能缓存预检请求的结果。此外,由于凭证的发送增加了安全风险,因此在配置时要格外小心,确保只对可信的源开放凭证权限。

Nginx 作为一款高性能的 HTTP 和反向代理服务器,可以用来处理跨域请求(CORS),关于Nginx相关的知识可以去看这篇文章:《Nginx——高性能Web服务器的基石,解锁Nginx的超级技能:从基础到实战的全方位指南》

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com