SpringBoot 跨域配置与过滤器实战——CORS、Filter、Interceptor 前后端分离项目中跨域问题是最常见的拦路虎。这篇讲 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 或方法上加注解RestControllerRequestMapping(/api/users)CrossOrigin(originshttp://localhost:8080)publicclassUserController{// 整个类都允许跨域}// 或者只允许某个方法跨域GetMapping(/{id})CrossOrigin(origins*)// 允许所有来源publicResultVOUserget(PathVariableLongid){returnResultVO.success(userService.getById(id));}缺点每个 Controller 都要加维护麻烦。方案二全局配置推荐ConfigurationpublicclassCorsConfigimplementsWebMvcConfigurer{OverridepublicvoidaddCorsMappings(CorsRegistryregistry){registry.addMapping(/api/**)// 允许跨域的路径.allowedOriginPatterns(*)// 允许的域名.allowedMethods(GET,POST,PUT,DELETE,OPTIONS).allowedHeaders(*).allowCredentials(true)// 允许携带 Cookie.maxAge(3600);// 预检请求缓存时间}}注意allowCredentials(true)和allowedOriginPatterns(*)必须同时使用不能单独用allowedOrigins(*)否则会报错。方案三Filter 手动处理ComponentOrder(1)publicclassCorsFilterimplementsFilter{OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{HttpServletResponseresp(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 典型场景请求日志ComponentOrder(2)publicclassRequestLogFilterimplementsFilter{privatestaticfinalLoggerlogLoggerFactory.getLogger(RequestLogFilter.class);OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{HttpServletRequestreq(HttpServletRequest)request;longstartSystem.currentTimeMillis();chain.doFilter(request,response);longdurationSystem.currentTimeMillis()-start;log.info({} {} {} - {}ms,req.getMethod(),req.getRequestURI(),response.getContentType(),duration);}}Interceptor 典型场景登录校验ComponentpublicclassLoginInterceptorimplementsHandlerInterceptor{OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{// 从请求头获取 TokenStringtokenrequest.getHeader(Authorization);if(tokennull||token.isEmpty()){response.setStatus(401);response.setContentType(application/json);response.getWriter().write({\code\:401,\message\:\未登录\});returnfalse;}// 校验 Token省略具体逻辑// if (!TokenUtil.validate(token)) { return false; }returntrue;}}ConfigurationpublicclassInterceptorConfigimplementsWebMvcConfigurer{AutowiredprivateLoginInterceptorloginInterceptor;OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){registry.addInterceptor(loginInterceptor).addPathPatterns(/api/**).excludePathPatterns(/api/login,/api/register,/doc.html);}}四、XSS 过滤器ComponentOrder(3)publicclassXssFilterimplementsFilter{OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{chain.doFilter(newXssHttpServletRequestWrapper((HttpServletRequest)request),response);}/** * 包装请求过滤 XSS 脚本 */staticclassXssHttpServletRequestWrapperextendsHttpServletRequestWrapper{publicXssHttpServletRequestWrapper(HttpServletRequestrequest){super(request);}OverridepublicStringgetParameter(Stringname){Stringvaluesuper.getParameter(name);returncleanXss(value);}OverridepublicString[]getParameterValues(Stringname){String[]valuessuper.getParameterValues(name);if(valuesnull)returnnull;String[]cleanednewString[values.length];for(inti0;ivalues.length;i){cleaned[i]cleanXss(values[i]);}returncleaned;}privateStringcleanXss(Stringvalue){if(valuenull)returnnull;// 过滤常见 XSS 关键字valuevalue.replaceAll(script,).replaceAll(/script,).replaceAll(onerror,).replaceAll(onclick,);returnvalue;}}}五、Filter 的执行顺序/** * 多个 Filter 的执行顺序 * * CorsFilterOrder(1)→ RequestLogFilterOrder(2)→ XssFilterOrder(3) * ↓ * DispatcherServlet → InterceptorpreHandle * ↓ * Controller * ↓ * InterceptorpostHandle * ↓ * InterceptorafterCompletion * ↓ * 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.withCredentialstrue;// 后端不能使用 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; } } }七、秒杀系统的跨域配置ConfigurationpublicclassSeckillWebConfigimplementsWebMvcConfigurer{OverridepublicvoidaddCorsMappings(CorsRegistryregistry){registry.addMapping(/api/**).allowedOriginPatterns(*).allowedMethods(GET,POST,PUT,DELETE).allowCredentials(true).maxAge(3600);}OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){registry.addInterceptor(newSeckillInterceptor()).addPathPatterns(/api/seckill/**).excludePathPatterns(/api/seckill/init/**);}}总结简单跨域 → CrossOrigin临时调试用 生产环境 → 全局 CORS 配置WebMvcConfigurer 特殊需求 → Filter 手动处理 权限校验 → InterceptorSpring 层面更灵活 觉得有用的话点赞 关注【张老师技术栈】吧每周更新 Java/Python/爬虫 实战干货不让你白来。

相关新闻

最新新闻

【AVDTP】规范精讲[1]: 蓝牙音视频传输的基石——AVDTP协议核心原理入门

【AVDTP】规范精讲[1]: 蓝牙音视频传输的基石——AVDTP协议核心原理入门

做蓝牙音视频开发的同学,一定绕不开AVDTP这个核心协议。不管是蓝牙耳机、蓝牙音箱,还是车载蓝牙的音视频投屏,底层的流传输、协商控制,全靠AVDTP在背后支撑。我在刚接触蓝牙音频开发时,总把A2DP和AVDTP搞混&#xff0c…

2026/7/14 5:32:06
小学二年级奥数完整知识点清单(分模块 + 必学内容 + 难易划分)

小学二年级奥数完整知识点清单(分模块 + 必学内容 + 难易划分)

文章目录一、计算模块(重中之重,奥数打底)1. 速算与巧算2. 数列与找规律填数3. 简单乘法巧算二、找规律(图形数字,二年级奥数必考)三、数图形(数线段、角、三角形、长方形)四、枚举与…

2026/7/14 5:32:06
基于 ESP32 与超声波阵列的停车场空余车位超声波编码定位系统设计与实现

基于 ESP32 与超声波阵列的停车场空余车位超声波编码定位系统设计与实现

一、系统概述 本系统采用 ESP32 为主控芯片,通过多路超声波传感器阵列对停车场各车位进行实时检测,利用超声波回波时间差 (ToF) 原理判断车位占用状态,并将车位编码信息通过 WiFi 上传至上位机或云端平台,实现停车场空余车位的智能化定位与管理。 二、硬件选型与接线 2.…

2026/7/14 5:32:06
在腾讯云 OpenCloudOS 9 上部署OCManager 上手实操:从零纳管到 AI 诊断,一次跑通完整闭环

在腾讯云 OpenCloudOS 9 上部署OCManager 上手实操:从零纳管到 AI 诊断,一次跑通完整闭环

在腾讯云 OpenCloudOS 9 上部署OCManager 上手实操:从零纳管到 AI 诊断,一次跑通完整闭环 前跑起来之后,怎么把它用起来。我会把主机纳管、命令下发、整机监控、AI 助手四大模块从头点一遍,途中踩到的 5 个真实 Bug 也会全部记录下…

2026/7/14 5:32:06
高压与低压系统互联中的TLP2770光耦应用与设计

高压与低压系统互联中的TLP2770光耦应用与设计

1. 高压与低压系统互联的核心挑战在工业自动化、电力电子和新能源领域,高压元件与低压控制设备之间的信号传输一直是个技术难点。我曾在多个光伏逆变器和电机驱动项目中,亲眼目睹过因隔离设计不当导致的设备损坏案例。最严重的一次,600V直流母…

2026/7/14 5:32:06
做了个内部知识库,部门人员都在用

做了个内部知识库,部门人员都在用

一、开场找一份产品手册平均 8 分钟,新人上手 2 周,客户问"上次那个配置怎么改的"群里 2 小时没人回。现在他们每天打开的第一个网站,是我做的内部知识库。二、需求是怎么来的团队规模不大,10 个人:5 个研发…

2026/7/14 5:27:06

月新闻