大家好,我是 V 哥,跨域问题是应用开发中比较常见的问题,比如前端应用和后端业务的端口不同,前端要向后端发送请求来获取数据,这个时候就会产生跨域问题,V 哥先从跨域问题的产生开始,来详细介绍跨域问题及解决方案。
CORS跨域问题的产生原因
CORS(Cross-Origin Resource Sharing,跨域资源共享)跨域问题源于浏览器的同源策略。同源策略是浏览器的一种安全机制,它要求浏览器在访问一个资源时,该资源的协议、域名和端口必须与当前页面的协议、域名和端口完全一致,否则就会被视为跨域请求,浏览器会对这类请求进行限制。
例如,当前页面的 URL 是 http://www.weige.com:8080
,如果该页面向 http://api.weige2.com:9090
发起请求,由于域名(www.weige.com
和 api.weige2.com
)和端口(8080
和 9090
)不同,就会触发跨域问题。
Spring Boot 处理 CORS 跨域问题的方法及案例
业务场景
假设我们有一个前端应用运行在 http://localhost:3000
,这是一个使用 React 构建的单页面应用。后端使用 Spring Boot 开发,运行在 http://localhost:8080
。前端需要向后端的 /api/users
接口发送请求来获取用户列表,由于前端和后端的端口不同,会产生跨域问题。
解决方法
1. 使用 @CrossOrigin
注解
@CrossOrigin
注解可以用于控制器类或控制器方法上,用于允许跨域请求。
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
import java.util.List;
@RestController
// 允许来自 http://localhost:3000 的跨域请求
@CrossOrigin(origins = "http://localhost:3000")
public class UserController {
@GetMapping("/api/users")
public List<String> getUsers() {
return Arrays.asList("Alice", "Bob", "Charlie");
}
}
在上述代码中,@CrossOrigin(origins = "http://localhost:3000")
注解表示允许来自 http://localhost:3000
的跨域请求访问该控制器中的所有方法。如果只需要允许某个方法跨域,可以将该注解添加到具体的方法上。
除了使用 @CrossOrigin
注解外,在 Spring Boot 中还可以通过以下几种方式解决 CORS 跨域问题:
2. 实现 WebMvcConfigurer 接口进行全局配置
通过实现 WebMvcConfigurer
接口并重写 addCorsMappings
方法,可以对整个 Spring Boot 应用进行全局的 CORS 配置。
示例代码
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 CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://weige.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
代码解释
addMapping("/**")
:表示对所有的请求路径都应用 CORS 配置。allowedOrigins("http://weige.com")
:指定允许访问的源站,这里仅允许http://weige.com
进行跨域访问。如果需要允许多个源站,可以传入多个参数。allowedMethods("GET", "POST", "PUT", "DELETE")
:指定允许的请求方法。allowedHeaders("*")
:允许所有的请求头。allowCredentials(true)
:允许携带凭证(如 Cookie)。maxAge(3600)
:预检请求的有效期,单位为秒。
3. 使用过滤器(Filter)
创建一个自定义的过滤器,在过滤器中设置 CORS 相关的响应头,以允许跨域请求。
示例代码
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "http://weige.com");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Max-Age", "3600");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
}
代码解释
- 在
doFilter
方法中,设置了一系列 CORS 相关的响应头,与前面通过WebMvcConfigurer
配置的含义类似。 - 对于
OPTIONS
请求(预检请求),直接返回 200 状态码,避免后续的业务逻辑处理。
4. 使用 Spring Security 进行 CORS 配置
如果项目中使用了 Spring Security,也可以在 Spring Security 的配置中进行 CORS 配置。
示例代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.Arrays;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.cors().configurationSource(corsConfigurationSource())
.and()
.authorizeRequests()
.anyRequest().permitAll()
.and()
.csrf().disable();
return http.build();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://weige.com"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
configuration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
代码解释
- 在
securityFilterChain
方法中,通过cors().configurationSource(corsConfigurationSource())
引入自定义的 CORS 配置。 corsConfigurationSource
方法创建了一个CorsConfigurationSource
实例,并设置了允许的源站、请求方法、请求头、凭证等信息。
这些方法都可以有效地解决 Spring Boot 中的 CORS 跨域问题,你可以根据项目的具体需求和架构选择合适的方式。
CORS跨域问题是否会影响网站的性能?
CORS跨域问题本身通常不会直接影响网站的核心性能指标,如响应时间、吞吐量等,但在处理CORS跨域的过程中以及由于跨域可能引发的一些情况,可能会对网站性能产生间接影响,具体如下:
增加请求次数
- 原因:在跨域请求时,浏览器可能会先发送预检请求(OPTIONS)来询问服务器是否允许该跨域请求。这额外的请求会增加网络往返次数,消耗一定的时间和网络资源。
- 影响:对于性能敏感的应用,特别是在移动网络等带宽有限、延迟较高的环境下,额外的请求可能会明显增加整体的请求响应时间,导致用户等待时间变长,影响用户体验。例如,一个实时数据更新的网页,若频繁进行跨域请求且每次都有预检请求,可能会使数据更新出现明显延迟。
增加服务器负载
- 原因:服务器需要处理额外的预检请求以及对跨域请求进行相应的配置和验证等操作。如果跨域请求量较大,服务器需要消耗更多的资源来处理这些请求,包括CPU、内存等。
- 影响:可能会导致服务器负载增加,响应速度变慢。当服务器负载过高时,甚至可能影响到其他正常请求的处理,导致整个网站的性能下降,出现响应卡顿、超时等问题。比如电商网站在促销活动期间,大量用户同时访问跨域的商品详情页等,若跨域处理不当,可能加重服务器负担,影响页面加载速度。
缓存策略受限
- 原因:由于跨域请求的复杂性,浏览器对跨域资源的缓存策略可能会受到限制。不同的浏览器对于跨域缓存的处理方式有所不同,可能会导致缓存命中率降低。
- 影响:缓存命中率降低意味着浏览器需要更频繁地从服务器获取资源,增加了网络流量和服务器负载,也会使页面加载速度变慢。例如,一个依赖跨域加载大量静态资源的网页,如果缓存受限,每次刷新页面都需要重新获取资源,会大大降低页面的加载性能。
安全机制导致性能损耗
- 原因:为了确保跨域请求的安全性,服务器和浏览器都需要执行一系列的安全检查和验证机制,如检查请求头中的源信息、验证凭证等。
- 影响:这些操作会消耗一定的计算资源和时间,在一定程度上可能会影响请求的处理速度和网站性能。不过,这种影响通常相对较小,在大多数情况下不会成为性能瓶颈,但在高并发、高性能要求的场景下,也可能会产生一定的累积效应。
最后
所以什么是跨域问题呢,V 哥来小结一下,跨域问题源于浏览器的同源策略,即浏览器在访问资源时要求该资源的协议、域名和端口与当前页面完全一致,否则视为跨域请求并进行限制。产生的原因是:不同源(协议、域名、端口任意一项不同)的页面之间进行资源请求时触发,如前端 http://localhost:3000 向 http://localhost:8080 的后端发起请求。解决方法可以使用注解方式或全局配置。需要注意的是,跨域本身不直接影响性能,但处理过程及相关情况会带来间接影响。关注威哥爱编程,全栈之路就你行。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。