bug现场谜之总不能告诉客户你要按F12(打开控制台)吧?(跨域详解)

bug现场谜之总不能告诉客户你要按F12(打开控制台)吧?(跨域详解)

💡 原文中文,约7000字,阅读约需17分钟。
📝

内容提要

文章分析前后端分离系统中页面刷新两次的问题。作者发现打开F12后问题消失,原因是浏览器缓存了前端JS。禁用缓存可复现:首次异步请求权限接口因无权限被302重定向至CAS单点登录获取service ticket,重新登录后再次请求权限接口,导致页面刷新两次。后台SpringBoot增加配置max-age:0禁用前端文件缓存后问题解决。文章后半部分讲解浏览器同源策略与CORS跨域机制,包括简单请求与复杂请求、preflight预检请求及响应头设置。

🔎

延伸解读

缓存与登录跳转的连锁反应

文章指出页面刷新两次的根源是前端JS被浏览器缓存。当异步请求权限接口时,因无权限被302重定向至CAS单点登录获取service ticket,重新登录后再次请求权限接口,导致页面刷新两次。打开F12后问题消失,是因为控制台默认禁用缓存,使JS每次重新加载,从而改变了请求时序。这提醒我们,缓存策略可能掩盖或改变认证流程中的问题。

后端配置解决缓存问题

作者在SpringBoot项目中通过配置`spring.resources.cache.cachecontrol.max-age: 0`禁用前端文件缓存,问题得以解决。这说明在前后端分离架构中,静态资源的缓存控制不仅影响性能,还可能影响依赖资源加载顺序的认证逻辑。当遇到难以复现的刷新或跳转异常时,可以优先检查浏览器缓存设置,并考虑在服务端调整缓存头。

同源策略与CORS的实用解读

文章后半部分解释了浏览器同源策略和CORS机制。同源要求协议、主机、端口完全一致,localhost和127.0.0.1也被视为不同源。跨域请求分为简单请求和复杂请求:简单请求直接发送,但响应若缺少Access-Control-Allow-Origin头,浏览器会阻止读取;复杂请求会先发送OPTIONS预检,服务端需正确返回Access-Control-Allow-Methods和Access-Control-Allow-Headers等头,浏览器才会放行实际请求。

预检请求的细节与常见误区

对于复杂跨域请求,浏览器自动发起OPTIONS预检,服务端必须响应允许的方法和头信息,否则实际请求会被阻止。预检响应中的Access-Control-Max-Age可缓存预检结果,默认5秒;Access-Control-Allow-Credentials则决定实际请求能否携带用户凭证。文章通过Crystal示例演示了缺少这些头时控制台报错的具体表现,帮助读者理解报错含义并正确配置服务端。

❓

Q&A

为什么打开F12控制台后,页面刷新两次的问题就消失了?

因为打开F12并勾选Network下的Disable cache后,浏览器不再缓存前端JS文件,从而避免了因缓存导致的异步权限请求失败和重定向,问题不再复现。

页面刷新两次的具体原因是什么?

前端JS被浏览器缓存,首次异步请求权限接口时因无权限被302重定向至CAS单点登录获取service ticket,重新登录后再次请求权限接口,导致页面刷新两次。

如何解决页面刷新两次的问题?

在后台SpringBoot项目中增加配置:spring.resources.cache.cachecontrol.max-age=0,禁用前端文件缓存,问题即可解决。

什么是浏览器的同源策略?

同源策略是浏览器的一种安全机制,要求协议、域名、端口三者完全相同才视为同源,否则限制读取跨域请求的资源。它于1995年在网景浏览器中引入,最初用于保护跨域DOM。

CORS中的简单请求和复杂请求有什么区别?

简单请求使用GET、POST或HEAD方法,且Content-Type限于application/x-www-form-urlencoded、multipart/form-data或text/plain,浏览器直接发送;复杂请求则需先发送OPTIONS预检请求,根据服务端响应头决定是否放行实际请求。

预检请求(preflight)需要服务端返回哪些响应头?

预检请求的响应必须包含Access-Control-Allow-Methods和Access-Control-Allow-Headers,且需匹配客户端实际请求的方法和头信息,否则实际请求会被浏览器阻止。此外,可选的Access-Control-Max-Age和Access-Control-Allow-Credentials分别用于缓存预检结果和允许携带凭证。

🏷️

标签

➡️

继续阅读