CORS 基础:跨域资源共享配置与原理
文章目录前言那个让我通宵的报错啥是CORS一句话说不清楚同源策略浏览器的被害妄想症CORS到底怎么工作的两种模式简单请求直接放行预检请求先问再问实战配置我怎么解决那个通宵问题Spring Boot后端配置Nginx反向代理方案生产环境推荐Node.js/Express配置我踩过的那些天坑血泪史坑1缓存导致的诡异现象坑2Vite热更新的锅坑3复杂请求体触发的预检安全警告别无脑开*总结与互动P.S. 目前国内还是很缺AI人才的希望更多人能真正加入到AI行业共同促进行业进步增强我国的AI竞争力。想要系统学习AI知识的朋友可以看看我精心打磨的教程 http://blog.csdn.net/jiangjunshow教程通俗易懂高中生都能看懂还有各种段子风趣幽默从深度学习基础原理到各领域实战应用都有讲解我22年的AI积累全在里面了。注意教程仅限真正想入门AI的朋友否则看看零散的博文就够了。前言那个让我通宵的报错兄弟们我先说个真事儿。去年三月份我接了个外包项目甲方爸爸要求前后端分离前端Vue3后端Spring Boot。我拍着胸脯说没问题三天搞定。结果第三天凌晨四点我还盯着控制台那一行红字发呆Access to XMLHttpRequest at ‘http://localhost:8080/api/login’ from origin ‘http://localhost:5173’ has been blocked by CORS policy…当时我就破防了这什么鬼东西我代码写得没毛病啊axios配置也对后端接口也能跑咋就数据拿不到呢后来天亮了我在楼下买了杯美式别问为什么是美式因为生活太苦了终于搞明白了——这特么就是传说中的跨域问题。今天这篇文章我不跟你讲大道理就聊聊这个让无数程序员掉头发的CORS到底是咋回事以及我怎么解决它的。啥是CORS一句话说不清楚首先啊CORS全称是Cross-Origin Resource Sharing翻译过来叫跨域资源共享。听着挺高大上的对吧其实就是浏览器的一种安保机制。我给你打个比方。想象你现在住在一个高档小区小区门口有保安大爷大爷手里拿着个花名册。按照小区规定只有业主同源能随便进出外来人员跨域要进小区必须得业主事先跟保安打过招呼登记备案才能放行。浏览器就是那个保安大爷。你的网站前端跑在http://localhost:5173这是你家。后端API跑在http://localhost:8080这是隔壁老王家。浏览器一看协议不同端口不同域名不同得了默认情况下就是不让进。等等你可能会问“那我直接用Postman调用接口咋就没问题”问得好这就是最坑的地方。CORS只针对浏览器你用Postman、用curl、写Python脚本随便调完全没限制。但一旦涉及到浏览器里的JavaScript发请求保安大爷立马警觉——“站住有嫌疑”同源策略浏览器的被害妄想症要说清楚CORS得先聊聊它背后的逻辑——同源策略Same-Origin Policy。这个策略是1995年Netscape浏览器搞出来的那时候互联网还挺单纯但网景公司就已经有被害妄想症了。他们担心如果一个恶意网站能通过JavaScript随便访问其他网站的数据那还得了比如你在A网站登录了银行账号然后不小心点开B网站B网站的JS就能直接调银行接口转钱这特么不是完犊子了所以同源策略规定协议、域名、端口三者必须完全一致才算同源。http://example.com:80/page1 和 http://example.com:80/page2 → 同源 ✓http://example.com:80/page1 和 https://example.com:80/page1 → 不同源协议不同 ✗http://example.com:80/page1 和 http://api.example.com:80/page1 → 不同源子域名不同 ✗http://example.com:80/page1 和 http://example.com:8080/page1 → 不同源端口不同 ✗看到没就这么严格。哪怕只是端口从80变成8080保安大爷立马翻脸不认人。但问题来了现代web开发哪有不分前后端的微服务架构下API跟前端部署在不同域名上是常态。这时候如果浏览器死守着同源策略不放那大家都不用干活了。所以CORS机制应运而生——它不是取消同源策略而是给了一个白名单机制告诉浏览器“这几家是我亲戚放行吧。”CORS到底怎么工作的两种模式好现在进入硬核其实也不硬部分。CORS请求分成两种类型简单请求和预检请求preflight。浏览器会自动判断走哪种流程你控制不了只能被动接受。简单请求直接放行啥算简单请求得同时满足下面一堆条件方法只能是GET、HEAD、POST之一头部字段只能有Accept、Accept-Language、Content-Language、Content-Type但Content-Type只能是application/x-www-form-urlencoded、multipart/form-data或text/plain不能有你自定义的Header说实话现在写代码谁还不带个Authorization头谁还不发个JSONapplication/json所以真实项目里简单请求几乎不存在我写了这么多年代码简单请求就遇到过一次还是静态资源加载。简单请求的流程是这样的浏览器直接发出请求但会在Header里加一个Origin字段告诉服务器“我来自http://localhost:5173给句话能不能访问”服务器如果愿意放行就在响应头里加Access-Control-Allow-Origin: http://localhost:5173或者更暴力的Access-Control-Allow-Origin: *浏览器收到响应一看哦允许了就把数据交给JavaScript。如果服务器没返回这个头或者返回的Origin不匹配浏览器就当场拦截你JavaScript拿到的就是个大红报错。预检请求先问再问这个才是我们日常开发的常态当你发一个POST请求Content-Type是application/json还带了Authorization头浏览器立马紧张起来“这请求有点复杂啊我得先探探路”于是在真正的请求之前浏览器会自动发一个OPTIONS请求这叫预检问服务器“大哥我打算用POST方法带这些头发JSON数据你行不行”这个OPTIONS请求会带三个关键头Origin: 还是表明来源Access-Control-Request-Method: POST我准备用的方法Access-Control-Request-Headers: content-type,authorization我准备带的头服务器这时候得表态返回Access-Control-Allow-Origin: http://localhost:5173 Access-Control-Allow-Methods: GET, POST, PUT, DELETE Access-Control-Allow-Headers: Content-Type, Authorization Access-Control-Max-Age: 86400看到没服务器明确说我允许这些方法允许这些头。浏览器一听妥了才会发出真正的POST请求。如果服务器没响应或者响应里没说允许浏览器就直接掐断真正的请求压根发不出去那个Access-Control-Max-Age是缓存时间单位秒。意思是这次预检结果我记住了86400秒内也就是24小时不用再问。实战配置我怎么解决那个通宵问题好了原理讲完了说说我是怎么解决凌晨四点的绝望的。Spring Boot后端配置我那个项目用的是Spring Boot 3.22025年的版本别用2.x了兄弟们该升级了。最暴力的方法在Controller上加注解RestControllerRequestMapping(/api)CrossOrigin(originshttp://localhost:5173,maxAge3600)publicclassUserController{// ... 你的代码}但这太麻烦了每个Controller都加我懒。所以全局配置ConfigurationpublicclassCorsConfigimplementsWebMvcConfigurer{OverridepublicvoidaddCorsMappings(CorsRegistryregistry){registry.addMapping(/**)// 所有路径.allowedOrigins(http://localhost:5173,http://127.0.0.1:5173).allowedMethods(GET,POST,PUT,DELETE,OPTIONS).allowedHeaders(*).allowCredentials(true)// 允许携带cookie.maxAge(3600);}}注意这里有个大坑allowedOrigins(“*”)和allowCredentials(true)不能同时用这是Spring Boot的安全限制。如果你需要带Cookie比如登录态必须指定具体域名不能用星号。Nginx反向代理方案生产环境推荐后来项目上线我换了种方案用Nginx做反向代理。这才是正解server { listen 80; server_name myapp.com; location /api { # 转发到后端服务 proxy_pass http://localhost:8080; # 关键是这几行 add_header Access-Control-Allow-Origin https://myapp.com always; add_header Access-Control-Allow-Methods GET, POST, PUT, DELETE, OPTIONS always; add_header Access-Control-Allow-Headers DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization always; # 处理预检请求 if ($request_method OPTIONS) { return 204; } } location / { proxy_pass http://localhost:5173; # 前端服务 } }看到没Nginx层就把CORS处理了后端代码完全不用改而且这样还能隐藏真实后端地址安全性。Node.js/Express配置如果你写Node是这样的constexpressrequire(express);constcorsrequire(cors);// 记得npm install corsconstappexpress();// 方案1允许所有开发环境凑合用app.use(cors());// 方案2生产环境严谨点app.use(cors({origin:[http://localhost:5173,https://production.com],methods:[GET,POST,PUT,DELETE],allowedHeaders:[Content-Type,Authorization],credentials:true,maxAge:86400}));Express这个cors中间件是2025年1月刚更新的2.8.5版本修复了几个安全漏洞记得升级。我踩过的那些天坑血泪史坑1缓存导致的诡异现象有一次我改了后端配置刷新了浏览器居然还是报错我以为是配置没生效重启了N遍服务差点把电脑砸了。后来才发现是Access-Control-Max-Age的锅。浏览器把之前的预检结果缓存了我改的配置它压根没去问解决方案开发环境把maxAge设成0或者手动禁用浏览器缓存Chrome DevTools里有个Disable Cache。坑2Vite热更新的锅用Vite的兄弟们注意Vite的热更新HMR用WebSocket有时候CORS配好了API请求但HMR还是连不上。这时候得在vite.config.js里单独配exportdefaultdefineConfig({server:{hmr:{overlay:false,},proxy:{/api:{target:http://localhost:8080,changeOrigin:true,}}}});用Vite的代理转发直接绕过浏览器的CORS这招在开发环境简直神器前端代码里请求/api/xxxVite帮你转发到8080端口浏览器看来都是5173同源完美坑3复杂请求体触发的预检我有一次发了个DELETE请求按理说也是简单方法对吧但浏览器还是发了OPTIONS预检。查了半天发现是因为我在DELETE请求里带了JSON body。某些浏览器说的就是Safari对DELETE带body的情况有特殊处理会触发预检。解决方案DELETE请求尽量把参数放URL里别放body里。或者老老实实让服务器支持OPTIONS响应。安全警告别无脑开*最后说点严肃的。我看到很多教程教人直接Access-Control-Allow-Origin:还配上Access-Control-Allow-Credentials: true。这特么是在玩火2025年OWASP发布的API安全Top 10里CORS配置错误依然排在第6位。如果你把开放到生产环境还允许携带Cookie那攻击者随便做个钓鱼网站就能冒充用户请求你的API这叫CSRF攻击的变种防不胜防正确的做法生产环境白名单必须明确指定域名内部系统考虑用IP白名单VPN别直接暴露在公网敏感接口转账、删数据就算开了CORS也要额外验证Referer和Origin头定期用curl -H Origin: https://evil.com测试你的API看有没有配置泄露总结与互动写到这儿天又快黑了我看了一眼时间下午5点25分。突然有种回到去年那个凌晨的错觉只不过现在手里是可乐而不是美式。CORS这个东西吧原理其实不复杂就是浏览器的门禁系统。但坑就坑在它涉及前后端、浏览器、服务器、网络层多个环节报错信息还模棱两可“blocked by CORS policy”——鬼知道是哪一步blocked的今天这篇文章我从那个让我通宵的报错说起聊了同源策略的被害妄想症拆解了简单请求和预检请求的区别给了Spring Boot、Nginx、Node.js的现成配置代码还倒了几盆我踩过的天坑。但是 我知道你们肯定还有自己的问题。比如我的情况更复杂用了网关微服务咋配或者WebSocket的CORS怎么处理“再或者为啥我配好了Postman能调浏览器还是不行”来评论区见把你遇到的CORS奇葩报错贴出来咱们一起研究。点赞过500的话我下周出个进阶篇专门讲复杂架构下的CORS治理方案还有怎么在微服务网关层Spring Cloud Gateway 2025新版统一处理跨域保证让你再也不怕那个红色报错最后转发这篇文章给你那个还在被CORS折磨的同事救人一命胜造七级浮屠啊朋友们P.S. 目前国内还是很缺AI人才的希望更多人能真正加入到AI行业共同促进行业进步增强我国的AI竞争力。想要系统学习AI知识的朋友可以看看我精心打磨的教程 http://blog.csdn.net/jiangjunshow教程通俗易懂高中生都能看懂还有各种段子风趣幽默从深度学习基础原理到各领域实战应用都有讲解我22年的AI积累全在里面了。注意教程仅限真正想入门AI的朋友否则看看零散的博文就够了。