从一个HTTP 302 重定向到数据类型不匹配的 bug 纠错过程
问题起源:一个简单的用户编辑功能
项目背景:我用Vue.js构建前端表单,用户可以添加或编辑用户信息,包括登录账号、密码、姓名、手机、邮箱,以及四个状态字段:accountNoExpired(账户未过期)、credentialsNoExpired(凭证未过期)、accountNoLocked(账户未锁定)、accountEnabled(账户启用)。这些状态字段在数据库中是Integer类型(1表示正常,0表示异常)。
前端提交代码用axios发送POST(添加)和PUT(编辑)请求到后端API /api/user。后端用Spring Security管理认证,带JWT token验证,还用Redis存储token。
一切看起来正常,但当我测试编辑用户(PUT请求)时,浏览器控制台显示请求返回302 Found,重定向到/login。奇怪的是,POST和GET请求都正常,只有PUT出问题。而且,F12网络面板显示重定向后,浏览器疯狂发送1000多个/login的OPTIONS请求,形成循环,导致页面卡死。
初始症状:
PUT /api/user 返回302,Location头指向/login。
随后/login的OPTIONS请求返回200,然后又一个OPTIONS返回302,循环不止。
后端日志没报异常,前端日志显示请求失败,但没具体错误。
我当时想:这是URL被拦截?还是token验证失败?或者Spring Security配置问题?
排查过程:从表面到深层,一步步试错
第一阶段:怀疑网络和配置问题
我先检查了基本的东西:
请求头:F12确认PUT请求带了Authorization头,token正确(从sessionStorage或localStorage获取)。
CORS配置:后端已允许所有方法和头,OPTIONS请求返回200,排除跨域问题。
代理/WAF:我没用Nginx或Cloudflare,直接本地运行,排除代理拦截。
试错1:我用Postman手动发送PUT请求,带token和数据,结果还是302。说明不是浏览器问题,是后端逻辑。
教训:调试时,先用工具如Postman隔离前端问题。
第二阶段:深入Spring Security和过滤器
我怀疑是认证问题,因为302重定向常见于未认证请求。
检查TokenVerifyFilter:这是自定义过滤器,验证JWT、Redis token匹配。如果失败,应该返回JSON错误(CodeEnum.TOKEN_IS_EXPIRED等),但我没看到JSON响应,而是302。
检查SecurityConfig:配置了formLogin,loginProcessingUrl是/api/login,permitAll()只允许/api/login,但默认登录页面是/login,未明确permitAll(),导致/login被拦截。
试错2:我修改SecurityConfig,添加/login到permitAll(),并在TokenVerifyFilter放行/login和OPTIONS请求。结果:循环减少,但PUT还是302。
试错3:禁用CSRF(已禁用),检查是否不支持PUT方法。但后端有@PutMapping,文档支持。
试错4:添加日志到TokenVerifyFilter,确认token收到且Redis匹配。日志显示验证通过,但还是302。说明过滤器放行后,Spring Security后续逻辑出问题。
错误修改:我一度怀疑是formLogin配置问题,试着设置loginPage("/api/login"),但没解决,因为问题不是登录端点。
教训:重定向循环往往是配置问题,但要区分loginProcessingUrl(处理登录)和loginPage(显示页面)。
第三阶段:分析重定向细节
F12网络面板成了救星:
第一:OPTIONS /api/user 200(预检)。
第二:PUT /api/user 302,带token和JSON负载。
第三:OPTIONS /login 200。
第四:OPTIONS /login 302,循环开始。
为什么/login收到OPTIONS?OPTIONS是CORS预检,通常为复杂请求如PUT。但/login应该是GET或POST,为什么预检?
试错5:怀疑前端FormData导致Content-Type是multipart/form-data,而后端期望application/json。切换为JSON发送,但还是302。
错误修改:我改了前端用普通对象发送JSON,但没注意字段值是"正常"(字符串),后端是Integer,导致解析失败。
教训:看F12时,不仅看状态码,要看负载和响应头(Location)。也检查数据类型匹配。
第四阶段:最终发现——数据类型不匹配
我盯着前端负载:
json
{
"id": 2,
"loginAct": "yuyan",
"loginPwd": "",
"name": "于嫣啊",
"phone": "17438374938",
"email": "yuyan@163.com",
"accountNoExpired": "正常",
"credentialsNoExpired": "正常",
"accountNoLocked": "正常",
"accountEnabled": "正常"
}
后端UserQuery类:
java
public class UserQuery {
// ...
private Integer accountNoExpired;
// ...
}
啊哈!"正常"是字符串,Spring解析@RequestBody时无法转为Integer,抛出HttpMessageNotReadableException。后端没捕获这个异常,默认转为302重定向到/login。
确认:我查了Spring文档,JSON解析失败会触发错误处理,如果没自定义,会重定向。
解决:修改前端:
javascript
formData.append('accountNoExpired', this.addUser.accountNoExpired == "正常" ? "1" : "0");
// 类似其他字段
测试:PUT成功,返回200!循环消失。
为什么用FormData有效?因为multipart/form-data用字符串提交,Spring自动转为Integer("1" -> 1),而JSON严格匹配类型。
- 微信
- 赶快加我聊天吧

- 赶快加我聊天吧
