SpringBoot过滤器与跨域配置的三种方案
前后端分离项目中,跨域问题是最常见的拦路虎。这篇讲 SpringBoot 中三种解决跨域的方式,以及 Filter 和 Interceptor 的区别与使用场景。
一、什么是跨域
1. 跨域的产生
前端地址:http://localhost:8080
后端地址:http://localhost:9090
↑ 端口不同,产生了跨域
浏览器的同源策略:
协议相同、域名相同、端口相同 → 同源
任何一个不同 → 跨域
2. 跨域的表现
浏览器控制台报错: Access to XMLHttpRequest at 'http://localhost:9090/api/users' from origin 'http://localhost:8080' has been blocked by CORS policy
二、三种跨域解决方案
方案一:@CrossOrigin 注解
最简单的方式,在 Controller 或方法上加注解:
@RestController
@RequestMapping("/api/users")
@CrossOrigin(origins = "http://localhost:8080")
public class UserController {
// 整个类都允许跨域
}
// 或者只允许某个方法跨域
@GetMapping("/{id}")
@CrossOrigin(origins = "*") // 允许所有来源
public ResultVO<User> get(@PathVariable Long id) {
return ResultVO.success(userService.getById(id));
}
缺点: 每个 Controller 都要加,维护麻烦。
方案二:全局配置(推荐)
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**") // 允许跨域的路径
.allowedOriginPatterns("*") // 允许的域名
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true) // 允许携带 Cookie
.maxAge(3600); // 预检请求缓存时间
}
}
注意: allowCredentials(true) 和 allowedOriginPatterns("*") 必须同时使用,不能单独用 allowedOrigins("*"),否则会报错。
方案三:Filter 手动处理
@Component
@Order(1)
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse resp = (HttpServletResponse) response;
resp.setHeader("Access-Control-Allow-Origin", "*");
resp.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
resp.setHeader("Access-Control-Allow-Headers", "*");
resp.setHeader("Access-Control-Max-Age", "3600");
// 预检请求直接返回
if ("OPTIONS".equalsIgnoreCase(((HttpServletRequest) request).getMethod())) {
resp.setStatus(HttpServletResponse.SC_OK);
return;
}
chain.doFilter(request, response);
}
}
三、Filter vs Interceptor
| 对比 | Filter(过滤器) | Interceptor(拦截器) |
|---|---|---|
| 层级 | Servlet 层面 | Spring 层面 |
| 范围 | 所有请求 | 只有进入 Controller 的请求 |
| 操作 | HttpServletRequest/Response | 方法调用前后处理 |
| 使用场景 | CORS、编码、鉴权 Token 校验 | 登录校验、日志记录、权限验证 |
Filter 典型场景:请求日志
@Component
@Order(2)
public class RequestLogFilter implements Filter {
private static final Logger log = LoggerFactory.getLogger(RequestLogFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
long start = System.currentTimeMillis();
chain.doFilter(request, response);
long duration = System.currentTimeMillis() - start;
log.info("{} {} {} - {}ms",
req.getMethod(),
req.getRequestURI(),
response.getContentType(),
duration
);
}
}
Interceptor 典型场景:登录校验
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// 从请求头获取 Token
String token = request.getHeader("Authorization");
if (token == null || token.isEmpty()) {
response.setStatus(401);
response.setContentType("application/json");
response.getWriter().write("{\"code\":401,\"message\":\"未登录\"}");
return false;
}
// 校验 Token(省略具体逻辑)
// if (!TokenUtil.validate(token)) { return false; }
return true;
}
}
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/api/**")
.excludePathPatterns("/api/login", "/api/register", "/doc.html");
}
}
四、XSS 过滤器
@Component
@Order(3)
public class XssFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
chain.doFilter(new XssHttpServletRequestWrapper(
(HttpServletRequest) request), response);
}
/**
* 包装请求,过滤 XSS 脚本
*/
static class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
return cleanXss(value);
}
@Override
public String[] getParameterValues(String name) {
String[] values = super.getParameterValues(name);
if (values == null) return null;
String[] cleaned = new String[values.length];
for (int i = 0; i < values.length; i++) {
cleaned[i] = cleanXss(values[i]);
}
return cleaned;
}
private String cleanXss(String value) {
if (value == null) return null;
// 过滤常见 XSS 关键字
value = value.replaceAll("<script>", "")
.replaceAll("</script>", "")
.replaceAll("onerror", "")
.replaceAll("onclick", "");
return value;
}
}
}
五、Filter 的执行顺序
/** * 多个 Filter 的执行顺序: * * CorsFilter(@Order(1))→ RequestLogFilter(@Order(2))→ XssFilter(@Order(3)) * ↓ * DispatcherServlet → Interceptor(preHandle) * ↓ * Controller * ↓ * Interceptor(postHandle) * ↓ * Interceptor(afterCompletion) * ↓ * Filter(反向执行) */
执行顺序:
请求进来 → CorsFilter.doFilter() → RequestLogFilter.doFilter() → XssFilter.doFilter() → LoginInterceptor.preHandle() → Controller 方法 → LoginInterceptor.afterCompletion() → XssFilter.doFilter() 结束 → RequestLogFilter.doFilter() 结束 → CorsFilter.doFilter() 结束 → 响应返回
六、常见问题
1. OPTIONS 预检请求
浏览器在发送真正的跨域请求之前,会先发一个 OPTIONS 请求 检查服务器是否允许跨域 如果 OPTIONS 请求没有正常返回,真正的请求也不会发出
2. 携带 Cookie 的跨域
// 前端需要设置
axios.defaults.withCredentials = true;
// 后端不能使用 allowedOrigins("*")
// 必须指定具体的域名
allowedOriginPatterns("http://localhost:8080")
allowCredentials(true)
3. Nginx 解决跨域
server {
listen 80;
location /api/ {
proxy_pass http://localhost:9090/;
# 跨域配置
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
if ($request_method = 'OPTIONS') {
return 204;
}
}
}七、秒杀系统的跨域配置
@Configuration
public class SeckillWebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOriginPatterns("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowCredentials(true)
.maxAge(3600);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SeckillInterceptor())
.addPathPatterns("/api/seckill/**")
.excludePathPatterns("/api/seckill/init/**");
}
}
总结
简单跨域 → @CrossOrigin(临时调试用) 生产环境 → 全局 CORS 配置(WebMvcConfigurer) 特殊需求 → Filter 手动处理 权限校验 → Interceptor(Spring 层面更灵活)
到此这篇关于SpringBoot过滤器与跨域配置的三种方案的文章就介绍到这了,更多相关SpringBoot过滤器与跨域配置内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
关于idea-web.xml版本过低怎么生成新的(web.xml报错)问题
今天通过本文给大家分享idea-web.xml版本过低怎么生成新的(web.xml报错)问题,通过更换web.xml版本解决此问题,感兴趣的朋友跟随小编一起看看吧2021-07-07
解决Java编译时错误:A JNI error has occurred,ple
这篇文章主要介绍了解决Java编译时错误:A JNI error has occurred,please check your installation and try again,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教2024-02-02


最新评论