文章目录
SpringBoot 如何处理跨域请求?你能说出几种方法?
在现代的Web开发中,跨域请求(Cross-Origin Resource Sharing,CORS)是一个常见的挑战。随着前后端分离架构的流行,前端应用通常运行在一个与后端 API 不同的域名或端口上,这就导致了浏览器的同源策略(Same-Origin Policy)的限制,从而出现了跨域请求问题。
Spring Boot 作为一种流行的 Java 后端框架,提供了多种处理跨域请求的方法,使得开发人员能够灵活地配置和管理跨域资源共享。本文将深入探讨几种常见的解决方案,帮助开发人员理解如何在 Spring Boot 应用中有效地处理跨域请求问题。
跨域请求概述
-
什么是跨域请求?
跨域请求(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 | 不同域名 | 不允许 |
-
为什么会出现跨域问题?
跨域问题的出现主要是为了保护用户数据和用户隐私安全。如果没有同源策略的限制,恶意网站可以利用当前用户的身份在其他网站上进行操作,如发送请求、读取数据,这可能导致信息泄露或潜在的安全威胁。
-
可能会导致的安全风险
- 信息泄露:允许恶意网站读取其他网站的敏感数据。
- 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 {
@Override
public 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 {
@Override
public 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 {
@Bean
public 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();
}
@Bean
CorsConfigurationSource 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 {
@Bean
public 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 {
@Override
public void customize(CorsConfiguration corsConfiguration) {
corsConfiguration.setMaxAge(3600L);
}
}
}
请注意,这些示例代码需要根据你的具体需求进行调整。例如,你可能需要限制允许的源,或者修改其他CORS设置。
补充
1. 预检请求(Preflight Requests)
跨域请求中,某些复杂请求(如带有自定义头部的请求、使用某些特殊方法如PUT
、DELETE
等的请求)会触发浏览器先发送一个预检请求(OPTIONS
请求)到服务器,以确定是否允许实际的请求。预检请求包含以下头部信息:
Origin
:表明发起请求的源。Access-Control-Request-Method
:实际请求将使用的HTTP方法。Access-Control-Request-Headers
:实际请求将使用的自定义头部。
服务器需要正确响应预检请求,以确保浏览器安全地执行实际请求。
处理预检请求的方法:
-
使用
@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
注解指定允许的源、方法和头部。 -
通过
WebMvcConfigurer
配置类处理:@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins("http://allowed-origin.com") .allowedMethods("GET", "POST") .allowedHeaders("header1", "header2") .exposedHeaders("header3") .allowCredentials(true) .maxAge(3600); } @Override public 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(凭证)的处理:
如果请求需要使用凭证(如使用Cookie
、HTTP
认证信息等),需要确保在跨域请求中设置Access-Control-Allow-Credentials
为true
,并在客户端请求中设置withCredentials
为true
。
暴露自定义头部:
如果需要在响应中暴露某些自定义头部供客户端访问,可以通过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的超级技能:从基础到实战的全方位指南》
标签:http,请求,Spring,全攻略,跨域,CORS,com,SpringBoot From: https://blog.csdn.net/weixin_68020300/article/details/140514936