# 处理跨域
询问了 ds r1 怎么解决跨域,他给了我以下几个方法(只测试了第二种,其他的先懒得测了):
(从这里开始是 ds 说的)
在 Spring Boot 中处理跨域请求可以通过以下几种方法实现,以下是详细的配置步骤和示例:
# 方法一:使用 @CrossOrigin
注解(局部配置)
适用于对单个 Controller 或方法设置跨域规则。
步骤:
- 在 Controller 类或方法上添加
@CrossOrigin
注解
可配置允许的来源、方法、头部等。
示例代码:
@RestController | |
@RequestMapping("/api") | |
@CrossOrigin(origins = "http://localhost:3000", | |
methods = {RequestMethod.GET, RequestMethod.POST}, | |
allowedHeaders = "*", | |
allowCredentials = "true") | |
public class MyController { | |
@GetMapping("/data") | |
public ResponseEntity<String> getData() { | |
// ... ... | |
} | |
} |
参数说明:
origins
: 允许的源列表(如"http://example.com"
)methods
: 允许的 HTTP 方法(如GET
,POST
)allowedHeaders
: 允许的请求头(默认为所有)allowCredentials
: 是否允许携带凭证(如 Cookies)
# 方法二:全局配置(推荐)
通过实现 WebMvcConfigurer
接口统一配置跨域规则。
步骤:
2. 创建全局跨域配置类
使用 @Configuration
注解标记,并实现 WebMvcConfigurer
。
示例代码:
@Configuration | |
public class CorsConfig implements WebMvcConfigurer { | |
@Override | |
public void addCorsMappings(CorsRegistry registry) { | |
registry.addMapping("/**") // 匹配所有路径 | |
.allowedOrigins("http://localhost:3000") // 允许的源 | |
.allowedMethods("*") // 允许所有 HTTP 方法 | |
.allowedHeaders("*") // 允许所有请求头 | |
.allowCredentials(true) // 允许携带凭证 | |
.maxAge(3600); // 预检请求缓存时间(秒) | |
} | |
} |
# 方法三:结合 Spring Security 的跨域配置
如果项目使用了 Spring Security,需额外配置。
步骤:
3. 在 Security 配置中启用 CORS 并禁用 CSRF(如需携带凭证需开启)
4. 确保全局 CORS 配置生效。
示例代码:
@Configuration | |
@EnableWebSecurity | |
public class SecurityConfig extends WebSecurityConfigurerAdapter { | |
@Override | |
protected void configure(HttpSecurity http) throws Exception { | |
http.cors().and() // 启用 CORS 支持 | |
.csrf().disable() // (可选)关闭 CSRF 保护 | |
.authorizeRequests() | |
.anyRequest().authenticated(); | |
} | |
// 若使用全局 CORS 配置,需显式声明 CorsConfigurationSource | |
@Bean | |
public CorsConfigurationSource corsConfigurationSource() { | |
CorsConfiguration config = new CorsConfiguration(); | |
config.setAllowedOrigins(Arrays.asList("http://localhost:3000")); | |
config.setAllowedMethods(Arrays.asList("GET", "POST")); | |
config.setAllowedHeaders(Arrays.asList("*")); | |
config.setAllowCredentials(true); | |
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); | |
source.registerCorsConfiguration("/**", config); | |
return source; | |
} | |
} |
# 方法四:使用 Filter 手动配置
指定一个 Filter 来设置响应头,灵活但需谨慎。
步骤:
5. 创建 CORS 过滤器并注册
手动设置响应头处理跨域。
示例代码:
@WebFilter("/*") | |
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", "http://localhost:3000"); | |
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"); | |
chain.doFilter(req, res); | |
} | |
} |
注册 Filter:
@Configuration | |
public class FilterConfig { | |
@Bean | |
public FilterRegistrationBean<CorsFilter> corsFilter() { | |
FilterRegistrationBean<CorsFilter> registration = new FilterRegistrationBean<>(); | |
registration.setFilter(new CorsFilter()); | |
registration.addUrlPatterns("/*"); | |
return registration; | |
} | |
} |
# 常见问题解决
- 预检请求(OPTIONS)被拦截
- 确保 Spring Security 放行 OPTIONS 方法:
http.authorizeRequests().antMatchers(HttpMethod.OPTIONS).permitAll();
- 确保 Spring Security 放行 OPTIONS 方法:
allowCredentials=true
但allowedOrigins
包含通配符*
- 需指定具体域名如
.allowedOrigins("http://localhost:3000")
。
- 需指定具体域名如
# 最佳实践建议
- 开发环境:允许所有来源(
allowedOrigins("*")
),快速调试。 - 生产环境:精确配置
allowedOrigins
以增强安全性。 - 结合全局配置和 Spring Security:确保多层级安全控制。
- 日志与测试:使用 Postman 或浏览器开发者工具验证响应头是否正确包含 CORS 信息。
按需选择配置方式,推荐使用 全局配置(方法二) 搭配 Spring Security(方法三),既统一管理,又兼顾安全性。
(ds 说的结束)
# 多路径放行
测试了第二个方法,发现 ds 给我的代码只能指定 ip 及端口号处理跨域。以前我 midway 直接配置一下就全都放行了。当然,指定路径肯定更安全。
尝试直接把放行路径改成 * ,出了问题,故问通义灵码,以下是回答:
从错误信息来看,问题出在 CORS(跨域资源共享)配置上。具体来说,当 allowCredentials
设置为 true
时, allowedOrigins
不能包含通配符 *
,因为这会导致 Access-Control-Allow-Origin
响应头无法正确设置。要解决这个问题,有两种方法:
- 显式列出允许的源:将具体的域名添加到
allowedOrigins
中。 - 使用
allowedOriginPatterns
:这是 Spring 5.3 引入的新特性,允许你使用模式来匹配多个源。
下面是具体的代码示例,展示了如何进行这两种配置:
# 方法一:显式列出允许的源
import org.springframework.context.annotation.Bean; | |
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 WebConfig implements WebMvcConfigurer { | |
@Override | |
public void addCorsMappings(CorsRegistry registry) { | |
registry.addMapping("/**") | |
.allowedOrigins("https://example.com", "https://another-example.com") // 显式列出允许的源 | |
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") | |
.allowCredentials(true); | |
} | |
} |
# 方法二:使用 allowedOriginPatterns
import org.springframework.context.annotation.Bean; | |
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 WebConfig implements WebMvcConfigurer { | |
@Override | |
public void addCorsMappings(CorsRegistry registry) { | |
registry.addMapping("/**") | |
.allowedOriginPatterns("https://*.example.com", "https://*.another-example.com") // 使用模式匹配多个源 | |
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") | |
.allowCredentials(true); | |
} | |
} |
# 解释
allowedOrigins
:用于指定允许的源域名。如果需要支持多个域名,可以逐一列出。allowedOriginPatterns
:用于指定允许的源域名模式。这种方式更加灵活,适用于多个子域名或特定模式的域名。allowCredentials(true)
:允许凭证(如 Cookie、HTTP 认证信息等)随请求发送。
选择其中一种方法进行配置即可解决问题。推荐使用allowedOriginPatterns
,因为它更灵活且易于维护。
# 放行 localhost
又问了一下怎么可以放行全部 localhost,通义灵码给了回答:
如果你想要允许所有来自 localhost
的源,可以使用通配符 *
来匹配所有端口。但是需要注意的是,使用 *
允许所有源时, allowCredentials(true)
是不允许的,因为这会引发安全问题。
如果你确实需要允许所有 localhost
的源并且允许携带凭证,你需要明确指定每个可能的端口,或者在开发环境中暂时放宽限制。以下是两种方法:
指定所有可能的
localhost
端口:@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:3000", "http://localhost:8080", "http://localhost:8081") // 添加所有可能的端口
.allowedMethods("*")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
在开发环境中允许所有源(不推荐用于生产环境):
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*") // 允许所有源
.allowedMethods("*")
.allowedHeaders("*")
.allowCredentials(false) // 不允许携带凭证
.maxAge(3600);
}