java 面试题:如何实现跨域?
在 Java 中实现跨域(Cross-Origin Resource Sharing,CORS)主要有以下几种方式,具体选择取决于应用场景和架构设计:
一、前端代理(开发环境)
在开发环境中,可通过前端脚手架(如 Vue CLI、Create React App)配置代理服务器,将请求转发到后端 API。
优点:无需修改后端代码,仅开发环境使用。
缺点:生产环境需另寻方案。
示例(Vue CLI):
// vue.config.js
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://backend-api.com', // 后端API地址
changeOrigin: true,
pathRewrite: { '^/api': '' }
}
}
}
}
二、后端配置(生产环境主流方案)
1. Spring Boot(推荐)
通过添加@CrossOrigin注解或全局配置实现。
方法一:Controller 级别(局部)
@RestController
@RequestMapping("/api")
public class UserController {
// 允许指定域名跨域
@CrossOrigin(origins = "http://example.com")
@GetMapping("/users")
public List<User> getUsers() {
return userService.getAllUsers();
}
}
方法二:全局配置(推荐)
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 所有接口
.allowedOrigins("*") // 允许所有域名
.allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的方法
.allowedHeaders("*") // 允许的请求头
.allowCredentials(true) // 允许携带Cookie
.maxAge(3600); // 预检请求缓存时间
}
}
2. Filter 配置(非 Spring 项目)
自定义 Filter 拦截所有请求并添加 CORS 响应头。
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
response.setHeader("Access-Control-Allow-Credentials", "true");
chain.doFilter(req, res);
}
}
在web.xml中注册 Filter:
<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>com.example.CorsFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
三、Nginx 反向代理(生产环境)
通过 Nginx 将前端和后端请求代理到同一域名下,避免跨域问题。
配置示例:
server {
listen 80;
server_name example.com;
# 前端静态资源
location / {
root /path/to/frontend;
index index.html;
}
# 后端API代理
location /api/ {
proxy_pass http://backend-api-server:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
四、JSONP(兼容性方案)
利用<script>标签不受同源策略限制的特性,仅支持 GET 请求。
后端实现:
@GetMapping("/data")
public void getData(HttpServletRequest req, HttpServletResponse res) throws IOException {
String callback = req.getParameter("callback"); // JSONP回调函数名
String data = "{\"name\":\"John\",\"age\":30}"; // 返回数据
res.setContentType("application/javascript");
res.getWriter().write(callback + "(" + data + ")"); // 包装为函数调用
}
前端调用:
<script>
function handleData(data) {
console.log(data);
}
</script>
<script src="http://api.example.com/data?callback=handleData"></script>
五、CORS Filter 配置(细粒度控制)
对于复杂场景,可通过配置更细粒度的 CORS 策略。
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(Arrays.asList("http://example.com")); // 允许的域名
config.setAllowedMethods(Arrays.asList("GET", "POST")); // 允许的方法
config.setAllowCredentials(true); // 允许携带Cookie
config.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", config); // 仅对/api路径生效
return new CorsFilter(source);
}
六、注意事项
- 生产环境禁止allowedOrigins("*"):若需允许所有域名,应改用allowedOriginPatterns("*")并配合allowCredentials(true)。
- Cookie 与认证:若需携带 Cookie,需同时设置:后端:Access-Control-Allow-Credentials: true 且 allowedOrigins 不能为*。前端:fetch或axios中设置withCredentials: true。
- 预检请求(Preflight):复杂请求(如 PUT、DELETE、带自定义头)会先发送 OPTIONS 请求,需确保服务器正确响应。
总结
- 开发环境:优先使用前端代理。
- Spring 项目:推荐@CrossOrigin注解或全局配置。
- 非 Spring 项目:使用 Filter 配置。
- 生产环境:推荐 Nginx 反向代理或细粒度 CORS 配置。
- 兼容性需求:可使用 JSONP(仅 GET 请求)。