前言
大家好,我是Leo哥😎😎😎,今天我们开启一个新的专栏,聚焦于面试实战,带大家学习真实的公司面试题目,分析背后的技术要点和解决思路。本期我们将深入探讨 Spring Boot如何解决跨域问题 这一经典面试题,希望通过这个专题,帮助大家在面试中脱颖而出,同时夯实实际开发中的技术能力。
无论你是刚入行的后端开发新手,还是有一定经验的开发工程师,这个专题都将为你提供全面的技术解读和实用技巧。接下来,让我们一起来破解这个问题吧!
跨域问题
什么是跨域
跨域是指当一个网页从一个域(比如 example.com)发起请求,试图访问另一个域(比如 api.example.com)上的资源时,因浏览器的同源策略限制而导致的访问问题。同源策略是浏览器的一项安全机制,用于防止恶意网站窃取用户数据。
以下情况下,会被视为 跨域 :
- 协议不同:example.com 和 example.com
- 域名不同:example.com 和 api.example.com
- 端口不同:example.com:80 和 example.com:8080
同源策略
同源策略(Same Origin Policy)规定:
- 一个页面只能读取来自相同源的资源。
- 如果协议、域名、端口三者有任何一个不同,就会触发跨域问题。
源策略应用的场景
- Cookie、LocalStorage 等存储。
- DOM 和 JavaScript 脚本。
- AJAX 请求。
CORS
CORS(Cross-Origin Resource Sharing,跨域资源共享)是一种由 W3C 标准定义的跨域请求机制,允许浏览器通过设置 HTTP 请求头和响应头来实现跨域访问。CORS 是浏览器针对跨域请求的安全策略扩展,通过服务端显式声明哪些资源可以被特定来源访问,从而安全地实现跨域资源共享。
简单来说,CORS 是一种由服务端控制跨域请求的安全策略,通过设置特定的 HTTP 响应头,告诉浏览器是否允许该请求跨域访问资源。
SpringBoot如何解决跨域?
常见的跨域方式大体来说有几种。
- 全局配置CORS
- 使用@CrossOrigin注解
- 自定义过滤器
- Nginx 配置反向代理解决跨域
通过 WebMvcConfigurer 全局配置
如果希望为整个项目启用跨域支持,可以使用 WebMvcConfigurer 来配置。
代码示例
- 配置类代码:
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 GlobalCorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 匹配所有路径
.allowedOrigins("http://example.com", "http://another-domain.com") // 允许的跨域域名
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允许的请求方法
.allowedHeaders("*") // 允许的请求头
.allowCredentials(true) // 是否允许发送 Cookie
.maxAge(3600); // 预检请求的缓存时间
}
}
详细说明:
- addMapping("/**"):匹配所有请求路径。
- allowedOrigins("example.com"):允许的跨域来源,支持多个域名。
- allowedMethods:指定允许的 HTTP 方法(如 GET、POST 等)。
- allowedHeaders("*"):允许所有请求头。
- allowCredentials(true):允许客户端携带 Cookie。
- 测试代码:
@RestController
public class TestController {
@GetMapping("/test")
public String test() {
return "Global CORS Test Successful!";
}
}
启动后,访问 example.com 下的前端可以正常调用此接口。
通过 @CrossOrigin注解
Spring 提供了 @CrossOrigin 注解,用于在控制器或方法级别启用跨域支持。
- 单个控制器方法启用跨域:
@RestController
@RequestMapping("/api")
public class ExampleController {
@GetMapping("/hello")
@CrossOrigin(origins = "http://example.com", maxAge = 3600) // 设置允许跨域的来源和缓存时间
public String hello() {
return "Hello, CORS!";
}
}
- origins:指定允许跨域的域名(如 example.com)。
- maxAge:预检请求的缓存时间,单位为秒。
- 全控制器启用跨域:
@RestController
@RequestMapping("/api")
@CrossOrigin(origins = "http://example.com", allowedHeaders = "*", methods = {RequestMethod.GET, RequestMethod.POST})
public class ExampleController {
@GetMapping("/test")
public String getData() {
return "This is test api";
}
}
适用场景:
- 某些 API 特定需要跨域支持时使用。
- 不适用于全局跨域需求。
自定义过滤器
如果需要更高级的跨域控制,比如动态允许某些域名跨域,可以通过自定义 CorsFilter 来实现。
- 自定义 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 init(FilterConfig filterConfig) throws ServletException {
// 初始化方法,可选
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 强制类型转换为 HttpServletRequest 和 HttpServletResponse
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 添加 CORS 响应头
httpResponse.setHeader("Access-Control-Allow-Origin", "*"); // 允许所有来源访问
httpResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); // 允许的 HTTP 方法
httpResponse.setHeader("Access-Control-Allow-Headers", "Origin, Content-Type, Accept, Authorization"); // 允许的请求头
httpResponse.setHeader("Access-Control-Allow-Credentials", "true"); // 是否允许携带 Cookie
// 处理 OPTIONS 请求(CORS 预检请求)
if ("OPTIONS".equalsIgnoreCase(httpRequest.getMethod())) {
httpResponse.setStatus(HttpServletResponse.SC_OK);
return;
}
// 继续执行下一个过滤器链
chain.doFilter(request, response);
}
@Override
public void destroy() {
// 销毁方法,可选
}
}
- 注册过滤器(可选) 默认情况下,@Component 注解已经将过滤器注册到了容器中。如果需要精确控制过滤器的顺序,可以使用 FilterRegistrationBean。
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<CorsFilter> corsFilterRegistration() {
FilterRegistrationBean<CorsFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new CorsFilter());
registrationBean.addUrlPatterns("/*"); // 过滤所有 URL
registrationBean.setOrder(1); // 设置过滤器的优先级,数字越小优先级越高
return registrationBean;
}
}
测试与验证
测试方法
-
启动 Spring Boot 服务。
-
使用带有跨域请求的前端应用(例如,Vue.js 或 React)发起请求。
-
打开浏览器的开发者工具,查看请求和响应头,确保响应中包含以下头信息:
-
Access-Control-Allow-Origin
-
Access-Control-Allow-Methods
-
Access-Control-Allow-Headers
-
Access-Control-Allow-Credentials
-
注意事项
- Access-Control-Allow-Origin 设置:
- 如果需要限制特定的来源访问,可以将 "*" 替换为具体的域名,例如 "example.com"。
- 安全性:
- 如果允许所有来源访问(*),可能存在安全风险,尤其是在涉及敏感数据的场景下。
- OPTIONS 请求:
- 如果未正确处理 OPTIONS 请求,可能导致前端跨域失败。
Nginx配置反向代理解决跨域
有时跨域问题并非一定需要由 Spring Boot 处理,而是可以通过 Nginx 的反向代理解决。
配置示例
- Nginx 配置文件:
server {
listen 80;
server_name xxxxxx.com;
location /api/ {
proxy_pass http://backend-service:8080/; # 后端服务地址
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
add_header Access-Control-Allow-Headers "Content-Type, Authorization";
add_header Access-Control-Allow-Credentials true;
}
}
适用场景:
- 前端和后端通过同一域名访问。
- 使用 Nginx 进行跨域代理时性能更高。
总结
Spring Boot 提供了多种跨域解决方案,可根据需求选择合适的方式:
- 注解方式:简单场景适用。
- 全局配置:推荐用于大多数项目。
- 自定义过滤器:适用于复杂需求。
- Nginx 配置:在后端无控制权时使用。
通过这些方法,可以高效、安全地解决前后端分离开发中的跨域问题。