springboot 里的重定向,本质是服务端告诉浏览器“再去请求另一个地址”,而循环跳转报错“重定向次数过多”,根源通常是请求在几个路径之间反复 302,始终到不了最终页面。
🔄 核心机制:redirect 与 forward
- 重定向(redirect):服务端返回 302 +
location头,浏览器收到后重新发起请求。地址栏会变化,是两次 http 请求。 - 转发(forward):服务端内部处理完再返回,浏览器只发一次请求,地址栏不变。
表格
| 对比项 | forward 转发 | redirect 重定向 |
|---|---|---|
| http 请求次数 | 1 次 | 2 次及以上 |
| 浏览器地址栏 | 不变化 | 变化为重新请求的地址 |
| 数据传递 | 可共享 request 域数据 | 需要重新通过 url 或新会话传递 |
| 典型场景 | 请求预处理后转内部视图 | 登录跳转、短链接跳转、接口迁移 |
🚨 循环跳转:成因与定位
浏览器在连续二三十次 302 后仍没拿到 200 响应,就会提示“重定向次数过多”。后端日志通常干净无异常,因为请求在过滤器、安全配置和 controller 之间反复转圈。
快速定位:用 curl 看 location 链路
curl -v http://localhost:8080/order 2>&1 | grep -i location
手动看两三轮跳转,问题基本就清楚了:
- 第一次
location是/login,第二次还是/login→ 登录页被拦截了。 location在两家域名间横跳 → 网关或代理转发配置问题。
1. 从一个很多程序员都见过的现象说起:一直“加载中”,最后来一句“将您重定向的次数过多”
如果你启动一个springboot项目,在浏览器里打开页面,结果没看到业务页面,反倒被提示“将您重定向的次数过多”,这种体验足够让人破防。后台日志干干净净,连异常堆栈都没有,数据根本没进入controller方法。真正排查过的同学都知道,这类问题之所以隐蔽,是因为它跟普通业务报错不一样——没有一个报错会告诉你“刚刚发生了循环跳转”,只能靠自己去把整条请求链路拆开看。
围绕“springboot后端服务重定向”这个话题,我准备把重定向这件事彻底拆开。我们平时说的重定向,在web开发里其实包含三种完全不同的面貌:服务端主动返回的响应式跳转、安全框架和网关层自动做的“隐形重定向”、以及前端路由层面的跳转。很多人面试时把重定向背得滚瓜烂熟,真到项目里却连“为什么登录后跳转不过去”都排查不出来,往往就是没分清这三种场景。
这篇文章适合正在用springboot做接口开发或前后端分离项目的朋友,也适合准备springboot面试、想系统梳理重定向原理的同学。内容不绕弯子,直接按我实际排查过的场景来讲。
1.1 浏览器报“重定向次数过多”之前,网络层到底发生了什么
先看一次最简单的重定向长什么样。浏览器请求 /need-login ,服务端发现你未登录,返回一个302状态码,同时响应头里带着:
http/1.1 302 found location: /login
浏览器看到302,不会把响应当成最终结果,它会立刻重新发起一次对 /login 的请求。注意,这次新请求和上一次几乎无关,浏览器认的是 location 里面的地址。如果 /login 这个地址在服务端又被拦截,又返回302指向 /need-login ,那浏览器就会在 /need-login 和 /login 之间来回跳。
chrome等浏览器不会允许这种循环无限执行下去,一般尝试二三十次之后就会停止并提示“将您重定向的次数过多”。这个提示的真正含义不是“你的服务器挂了”,而是“你的服务端在逻辑上让浏览器没法到达一个最终页面”。很多后端同学这时习惯先去翻日志找报错,方向从一开始就错了——这事得从http跳转链路上找,光看业务日志是找不到根因的。
1.2 springboot里的重定向,本质是“告诉浏览器再去一次”
在springboot里做重定向,最简单的写法就是controller方法返回一个字符串:
@getmapping("/old")
public string redirectold() {
return "redirect:/new";
}
这段代码看着简单,但很多刚接触springboot的人会踩一个典型坑:如果类上标注的是 @restcontroller 而不是 @controller ,那么 return "redirect:/new" 并不会触发跳转,而是会把字符串 redirect:/new 作为json或纯文本返回给调用方。区别在于 @restcontroller 包含了 @responsebody 语义,springmvc认为你要返回的是数据本身,不再走视图解析逻辑。
所以真正要理解springboot里的重定向,不能只记住一个字符串前缀,得弄清楚它背后被谁解析、经过哪条处理链、最终落在哪个http响应上。下面我把这块机制完整拆一遍。
2. 核心机制拆解:redirect、forward 与参数传递,一次讲透
2.1 return "redirect:/xxx" 到底经历了什么
代码运行到 return "redirect:/new" 时,springmvc并不会直接发送一个响应头。它会把这个字符串交给视图解析器,由 urlbasedviewresolver 识别出 redirect: 前缀,然后创建一个 redirectview ,最终由 redirectview 调用 httpservletresponse.sendredirect() 来生成302响应。
如果你不需要视图解析,也可以更命令式地写:
@getmapping("/goto")
public void gotopage(httpservletresponse response) throws ioexception {
response.sendredirect("/target");
}
sendredirect 是servlet规范提供的标准方法,它做了三件事:把状态码设置为302、在 location 头中写入跳转地址、清空响应缓冲区。理解了这一点你就知道,任何重定向最终都只是“一个状态码加一个头”的组合,并没有更神秘的东西。
在springboot 2.7和springboot 3.x里,重定向的语义没有变化,主要区别在于servlet api换了包名,从 javax.servlet 变成 jakarta.servlet 。你如果是从老项目升级上来的,会发现 httpservletresponse 的导入路径变了,但 sendredirect 的用法完全没有变。实际项目里我更推荐使用 redirectview 或 responseentity 之类的封装方式,比直接操作 httpservletresponse 更容易做单元测试。
2.2 forward 与 redirect:一次“内部转交”,一次“对外通知”
很多同学会把转发和重定向混在一起,其实它们完全是两个层级的东西。重定向是“服务端让浏览器再去请求另一个地址”,是两次http请求;转发则是“服务端内部把这次请求转交给另一个资源处理”,浏览器从头到尾只发了一次请求,地址栏也不会变化。
用一个生活化类比来解释:转发像公司前台接到电话后,直接把电话转给内部对应同事,外部客户并不知道电话被转给了谁;重定向则像前台对客户说“请拨打另一个号码”,客户需要自己重新拨一次。
在springboot代码里,转发是这样写的:
@controller
public class forwardcontroller {
@getmapping("/source")
public string source() {
return "forward:/target";
}
}
两者对比起来非常清晰:
| 对比项 | forward 转发 | redirect 重定向 |
|---|---|---|
| http请求次数 | 1次 | 2次及以上 |
| 浏览器地址栏 | 不变化 | 变化为重新请求的地址 |
| 数据传递 | 可共享request域数据 | 需要重定向后重新通过url或新会话传递 |
| 执行方 | 服务端内部完成 | 浏览器参与完成 |
| 典型场景 | 请求预处理后转内部视图 | 登录跳转、短链接跳转、接口迁移 |
很多老项目喜欢在controller里用forward拼接jsp路径,但在前后端分离的今天,服务端jsp已经很少见了,forward更多出现在filter或安全组件内部。在springboot开发中,大部分跳转需求都应该用redirect,因为前端路由和后端接口往往是两个不同服务,只有让浏览器重新请求一次,才能把地址切到正确地地方。
2.3 带参数跳转的正确姿势:redirectattributes 与 flashmap
重定向最常见的一个坑,是参数传递。先看错误示范:
@getmapping("/login-success")
public string loginsuccess(httpsession session) {
session.setattribute("username", "zhangsan");
return "redirect:/home";
}
这种写法不是不能用,而是在分布式会话、redis共享会话的场景下会显得很笨重,并且把用户信息塞进httpsession会扩大会话作用域,退出登录或清缓存时容易留下残留数据。如果只是跳转后展示一次性消息,比如“操作成功”“保存完成”,更标准的做法是用 redirectattributes 。
@getmapping("/save")
public string save(@requestparam string name, redirectattributes attrs) {
// 保存业务逻辑
attrs.addflashattribute("message", "保存成功");
return "redirect:/list";
}
addflashattribute 把数据放在flashmap中,flashmap底层存在session里,但只存活一次重定向周期:重定向后的那次get请求能拿到数据,拿到之后立刻被清除。你可以把它理解成一个“只喂一次”的临时邮箱,信封送完就销毁,避免数据长期留在会话里。
相比之下,如果业务上必须让目标接口通过url获取参数,可以用 addattribute :
attrs.addattribute("page", 1);
return "redirect:/list";
这种方式会把参数拼到url后面,生成类似 /list?page=1 的地址。它适合分页、查询条件这类需要分享链接的业务参数;而提示消息这类用户态数据,尽量别放url里,别人只需要篡改参数就能制造一个假提示,体验和安全性都不好。
2.4 redirect 外链、编码与上下文路径,三种易踩边界
第一类边界是重定向到外部链接。直接写 return "redirect:" + url 时,如果url来自用户输入或第三方回调,风险马上暴露出来。攻击者可以把url改成 https://evil.com ,用户就被带去钓鱼站。这块后面专门讲防御方案,这里先记住:外链跳转必须有白名单校验,不能直接把参数当跳转目的地。
第二类边界是中文和特殊符号编码。假设跳转目标里有 nickname=张三 ,直接拼接字符串的话,生成的 location 头里的中文极易出现乱码或被浏览器截断。我会用 uricomponentsbuilder 来构造url,让框架把参数做一次规范编码:
@getmapping("/profile")
public string redirectprofile(@requestparam string nickname) {
uri uri = uricomponentsbuilder.frompath("/user/detail")
.queryparam("nickname", nickname)
.build()
.encode()
.touri();
return "redirect:" + uri;
}
第三类边界是 server.servlet.context-path 。如果项目配置了统一的上下文前缀,比如项目启动后是 http://localhost:8080/myapp ,那么所有重定向路径要不要带前缀,取决于你使用相对路径还是绝对路径。使用 redirect:/home 时,springboot会基于当前上下文自动拼前缀;如果本地测试拼了前缀,部署后容器上下文又变了,就可能出现404或302跳错地址。我建议统一写成不带头域名和上下文的前缀路径,让springboot自己去处理上下文,而不是硬编码全路径。
3. “将您重定向的次数过多”:成因、定位与三种典型现场
3.1 浏览器凭什么判定“次数过多”
要理解这个报错,先看一条理论上的合法跳转链:
/order -> /login?redirect=/order -> 登录成功 -> /order
这是两条302再加一次真实页面响应,链路是通的,浏览器不会报错。真正出问题的是下面这种:
/order -> /login?redirect=/order /login -> /order /order -> /login?redirect=/order /login -> /order
这就成了死循环。浏览器有自我保护机制,当同一地址反复以重定向方式出现且始终没有返回200,它会认定“重定向次数过多”,停止继续加载。
有趣的是,这个过程在后端日志里往往什么都看不到。不是后端没处理请求,而是请求在filter、security配置和controller之间反复转圈,每次都是“正常”的302,业务代码没有抛出任何异常。所以排障的第一步,必须是绕过后端日志,直接看浏览器或命令行工具看到的完整location链路。
3.2 现场一:拦截器把登录页也拦截了
这是我在新项目里见到最多的一种情况。开发同学写了一个拦截器判断用户是否登录,未登录就重定向到 /login ,但拦截器拦截范围是 /** ,自己又没有把 /login 排除掉。结果用户访问任意页面,被拦下来重定向到 /login , /login 再次被拦截器捕获,发现仍未登录,又重定向到 /login 自身。浏览器马上提示次数过多。
正常配置应该像这样:
@override
public void addinterceptors(interceptorregistry registry) {
registry.addinterceptor(new logininterceptor())
.addpathpatterns("/**")
.excludepathpatterns("/login", "/css/**", "/js/**", "/img/**", "/error");
}
这个场景背后有个很容易被忽略的小知识:浏览器请求 /login 页面时,如果html里引用了css、js等静态资源,而这些资源路径也被拦截器挡了,同样会被重定向到登录页,日志里就会出现大量对 /login 的302。静态资源没有登录状态,自然又触发一次重定向,即使没到“次数过多”的临界点,登录页也会被拖得很慢。所以 excludepathpatterns 里一定要把静态资源目录一并放行。
3.3 现场二:spring security 把登录路径排除漏了
用spring security做认证时,302循环有自己的特色。假设配置了表单登录,登录页是 /login ,但你拦截规则定义了所有请求都需要认证,并且没有对 /login 放行:
http.authorizehttprequests(auth -> auth.anyrequest().authenticated())
.formlogin(form -> form.loginpage("/login"));
当用户访问任意未认证地址时,security会重定向到 /login ; /login 本身又需要认证,又重定向回登录页,陷入了类似上面拦截器的循环。实际上spring security对登录页通常有默认的放行逻辑,但只要你手动指定了 loginpage ,就要确保对应的路径能被匿名访问:
http.authorizehttprequests(auth -> auth
.requestmatchers("/login", "/error").permitall()
.anyrequest().authenticated())
.formlogin(form -> form.loginpage("/login"));
spring boot 3.x里 antmatchers 被替换成了 requestmatchers ,很多从2.x升级上来的老配置改起来容易漏,这一漏就可能把登录页和登录接口一并锁住。这类问题排查时,不要只盯自己的controller,先看security过滤链里哪些路径被 permitall 了。
3.4 现场三:https 与 cookie 互相拉扯,302反复落在登录页
还有一个很“磨人”的场景,发生在https改造之后。系统前面有网关或负载均衡做https证书终止,后端服务实际跑在http上。此时如果服务端判断请求头里的 x-forwarded-proto 是 http ,就强制重定向到 https ,把用户跳到新地址。
问题也常出在这里:用户确实跳到了https,但浏览器里的登录cookie因为设置了 secure 属性,只允许在https请求中携带。如果反代转发时某些请求头没有正确透传,后端以为自己还在http环境,再次把用户重定向回或者不放行,登录状态一直建立不起来,最终就是反复在登录页和首页之间弹跳。
排障时要记得看完整的请求,而不只是java代码。用抓包工具或浏览器开发者工具看一下跳转链路上每一次 location 的地址、请求头里的 cookie 、响应头里的 set-cookie ,基本能发现是哪一环把协议或cookie属性搞错了。这类问题不是springboot语法问题,而是部署架构和框架配置共同作用的结果。
3.5 五分钟定位法:curl -v 看 location 链路
遇到重定向循环,我基本不动浏览器图形界面,而是用curl直接看响应头:
curl -v http://localhost:8080/order 2>&1 | grep -i location
如果加了 -l 参数,curl会跟随重定向继续请求,能看到每一步的去向;如果不加 -l ,只显示第一次302的 location 。手动看两三轮跳转,问题往往就清楚了。比如发现第一次 location 是 /login ,第二次还是 /login ,那基本就是登录页被拦截了;如果location在两家域名之间横跳,那就是网关或代理转发配置的问题。
浏览器里也可以借助开发者工具,勾选“preserve log”,然后刷新页面,把所有302记录按时间排一眼就能列出来。但我个人还是推荐curl,因为它排除掉了浏览器缓存、代理插件等干扰变量,看到的是最原始的http交互。
4. 认证、https 与安全:重定向背后最容易忽视的边界
4.1 前后端分离后,认证失效不该再“整个页面跳登录”
现在很多springboot项目做的是纯接口服务,前端是vue或react独立部署。这类项目的认证失效处理,和传统服务端渲染项目完全不同。
在传统项目里,登录态失效后重定向到 /login?redirect=原地址 非常合理,因为服务端可以控制整个页面跳转。但在前后端分离架构下,前端页面路由归前端管,后端如果直接返回302,让浏览器跳到后端的 /login 页面——很可能跳到一个根本不存在的前端页面或空白json接口。更合适的做法是返回401状态码,让前端axios统一拦截到 window.location 或路由跳转到前端登录页。
这在spring security里需要自定义认证入口点:
http.exceptionhandling(e -> e.authenticationentrypoint((request, response, authexception) -> {
response.setstatus(httpservletresponse.sc_unauthorized);
response.setcontenttype("application/json;charset=utf-8");
response.getwriter().write("{\"code\":401,\"message\":\"未登录或登录已过期\"}");
}));
这样一来,安全框架默认的302行为就被替换成了接口友好的json响应。很多人面试时被问“spring security登录过期怎么处理”,其实问的就是这个设计取舍:到底让服务端把用户强制拽到登录页,还是返回状态码交给前端做业务判断。在一个前后端分离系统里,答案通常偏向后一种。
4.2 强制 https 跳转:别把逻辑全压在后端
对于https跳转,我通常建议在网关或nginx层完成,尽量别在springboot业务代码里硬编码。因为后端不一定能准确感知外界采用的是http还是https,尤其当请求经过了层层转发之后,后端看到的协议可能已经被改写过。正确做法是前置代理统一处理:
server {
listen 80;
return 301 https://$host$request_uri;
}如果项目没有前置代理,非要用springboot接收http请求后跳转https,可以在配置里开启:
server:
port: 443
ssl:
enabled: true
key-store: classpath:keystore.p12
key-store-type: pkcs12但要注意,一个springboot应用同时监听80和443需要额外依赖或启动两个实例,这与直接用nginx比起来复杂得多。后端强行做https跳转,最常见的问题是某些回调地址、健康检查接口被强制重定向后,外部系统拿不到完整证书信息,进而误判服务不可用。所以能用网关解决的,就别去controller里写。
4.3 开放重定向漏洞:威胁比想象中更真实
在我审核过的代码里,最常见的重定向安全隐患是下面这种写法:
@getmapping("/redirect")
public string redirect(@requestparam string url) {
return "redirect:" + url;
}
攻击者只要构造一个链接 /redirect?url=https://phishing-site.com ,诱导用户点击,用户看到自己访问站的域名,信任度高,殊不知点完后会被带到钓鱼页面。这类攻击叫开放重定向,虽然不直接窃取数据,但常常被用来配合钓鱼、绕过waf、传播恶意链接,在安全审计里属于中高危问题。
防御思路很直接,先校验再跳转:
private static final list<string> allowed_hosts = arrays.aslist("example.com", "www.example.com");
@getmapping("/redirect")
public string saferedirect(@requestparam string url) {
try {
uri uri = uri.create(url);
if (!allowed_hosts.contains(uri.gethost())) {
throw new illegalargumentexception("非法跳转地址");
}
return "redirect:" + uri;
} catch (illegalargumentexception e) {
return "redirect:/error";
}
}这里不能只判断url是否以 / 开头就放行。很多经验不足的开发会写“只要不是http开头就安全”,实际上 //evil.com 、 https://evil.com 都能绕过简单判断。我自己实践下来,最稳妥的做法是两层校验:先解析uri拿到host,再比对白名单;如果允许相对路径跳转,也要确认路径不包含协议和外部主机名。这块宁可写得繁琐,也不要图省事。
4.4 顺手分享一个容易被忽略的状态码细节
302在历史上语义是“临时重定向”,但很多浏览器在302后用get请求访问location,不管原请求是post还是get。如果原接口是post,你预期重定向后的地址继续post,这并不会发生。需要保持原请求方法时,规范要求使用307或308状态码:307是临时重定向且保持原方法,308是永久重定向且保持原方法。
springboot中要返回307,使用 responseentity 比较直接:
return responseentity.status(httpstatus.temporary_redirect)
.location(uri.create("/new-location"))
.build();
但在日常业务中,post后返回302跳转到get结果页才是正常交互,不必刻意保持post。只有在做支付回调、api对接这类需要保持请求语义的场景,才需要认真考虑307。
5. 典型工程场景复盘:短链、接口迁移、登录回跳与网关
5.1 短链接跳转:一行 location 却要权衡 302 还是 301
短链服务是重定向最典型的落地场景。用户访问 /s/abc123 ,后端查到真实长地址,返回302让浏览器跳过去。用springboot实现一个极简版本:
@getmapping("/s/{shortcode}")
public responseentity<void> redirectshorturl(@pathvariable string shortcode) {
string longurl = shorturlservice.getlongurl(shortcode);
if (longurl == null) {
return responseentity.notfound().build();
}
return responseentity.status(httpstatus.found)
.location(uri.create(longurl))
.build();
}短链用302还是301,是一个很实际的取舍。301是永久重定向,浏览器和搜索引擎会缓存新的地址,第二次访问就不再请求短链服务,后端拿不到点击数据;302每次都会经过短链服务,方便统计点击量、做安全校验、临时调整目标地址。绝大多数短链系统都会用302。只有那种目标地址确定永不变化的内部链接,才适合用301。
另外,长地址可能指向外部网站,这里就回到了开放重定向的坑。只要短链不是完全内部使用,跳转前一定要做协议白名单和域名拦截,不能允许 file:// 、 javascript: 这类危险协议被带到location里。
5.2 旧接口迁移:用301告诉调用方“永久搬家”
系统重构时,经常遇到旧接口要下线但调用方来不及改的情况。比较稳妥的做法是保留旧路径,用301跳转到新接口。301的好处是语义足够清楚:告诉调用方“这个地址永久变动了”,网关、浏览器、http客户端都会自觉缓存新地址,后续请求直接打新接口,减轻旧路径的压力。
代码上可以直接返回301:
@getmapping("/v1/order")
public responseentity<void> legacyorderapi() {
return responseentity.status(httpstatus.moved_permanently)
.location(uri.create("/v2/order"))
.build();
}这里有一个必须提醒的点:301会被很多http客户端和浏览器永久缓存。如果某天新接口出问题,你想把流量引回旧路径,会发现客户端还在固执地访问旧地址对应的301缓存,根本不再请求你的服务。所以迁移初期没把握时用302,等确定新接口稳定了再切301,能给自己留出调整空间。
5.3 登录成功后回跳:必须自己做一次来源校验
很多系统会在用户未登录访问受保护页面时,拼接一个 redirect 参数把原地址带过去,登录成功后再跳回来。这个功能看似简单,但处理不好会变成安全隐患。
先看一个典型处理流程:
用户访问 /user/info 拦截器发现未登录 重定向到 /login?redirect=/user/info 用户登录成功后,服务端把用户带回 /user/info
问题在于,登录页接口如果直接用 string redirect = request.getparameter("redirect"); return "redirect:" + redirect; ,那用户完全可以构造一个恶意链接,诱导别人点“登录”后跳到外部钓鱼站。攻击形式虽然发生在登录后,本质上还是开放重定向。
修复方式是在登录成功后跳转前校验目标地址必须来自站内,比如要求以 / 开头且不是 // 开头的协议相对地址:
public boolean issafeinternalredirect(string targeturl) {
if (targeturl == null || !targeturl.startswith("/") || targeturl.startswith("//")) {
return false;
}
return true;
}如果系统包含多个业务子域,不能简单用“单斜杠开头”判断,可以把允许跳转的域名做成配置项,由运维维护白名单。登录回跳路径的校验属于看起来不起眼但绝不能省略的环节,安全测试的一抓一个准。
5.4 网关与文件下载场景中的302,需要单独设计
当你把springboot服务部署到网关后面,服务返回的 location 头可能会出现一个隐蔽问题:如果服务注册时暴露的是内网地址,返回302时 location 填的可能是内网地址,浏览器拿到这个地址后发现无法访问,表现就是“点击后页面打不开”或“下载失败”。
比如spring cloud gateway转发到某个微服务,微服务返回 location: http://service-internal:8080/new-path ,浏览器不认识 service-internal 这个主机名。解决办法通常是在网关层做一次 location 重写,把内网地址替换成网关对外域名。这个逻辑放在各微服务里统一处理太难了,用全局filter更合适。当你遇到“后端明明返回了302,浏览器却跳到内网地址”时,先检查有没有在网关层做location改写。
文件下载场景则是另一个方向。当你在浏览器里点击下载,服务端先302跳到oss临时地址,这个交互本身没问题。但如果前端是用fetch或axios下载文件,请求是异步发出去的,302发生在xhr或fetch内部,浏览器并不会自动跳转页面,你的前端代码拿到的可能是cors错误或空响应,而不是文件。处理方案是让下载接口直接返回文件流,或者由后端计算好临时下载地址后在json里返回,前端再用 window.open 或创建隐藏 <a> 标签触发浏览器原生下载。
6. 写在最后的排障习惯:我在实际项目里的几点感受
做后端这些年,我越来越觉得重定向本身不难,难的是链路里每一个环节都“看似正常”。服务端返回302是正常的,浏览器跟随302是正常的,重定向到登录页也是正常的,但当这些正常连接成一条闭环时,整个系统就卡死了。面对这类问题,我自己的排障习惯是:先不看代码,先看location链;不猜,用curl把每次重定向的真实地址打出来,往往两三轮跳转就能圈定问题范围。
还有一个特别容易被忽略的点:开发环境用 spring-boot-devtools 时,浏览器可能会缓存旧页面和旧js,有时候你改了代码、清了后端日志,前端还在跑旧的重定向逻辑,误以为没生效。遇到“明明改了配置还是循环重定向”的诡异问题,先强制刷新或换个无痕窗口试试,能排除一大堆误判。
最后再分享一个小技巧。如果你在一个复杂项目里被重定向问题折腾得焦头烂额,可以临时给 filter 加一条请求日志,把每次请求的uri、cookie和当前登录态打出来。也许你很快就能看到某一次请求连续进了同一个过滤器两次,这时问题也就有了答案。重定向的排查没有什么高深理论,就是多观察链路、多保留现场,一步一步推翻“正常假设”,答案自然会浮出来。
到此这篇关于spring boot重定向原理剖析及循环跳转问题定位指南的文章就介绍到这了,更多相关springboot重定向核心机制与循环跳转内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论