ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

若依二开:取消验证码与调整超时登录的完整配置指南

若依二开:取消验证码与调整超时登录的完整配置指南

做若依定制开发的朋友,十有八九会遇到两个绕不开的需求:取消登录验证码、调整超时登录策略。这两个需求看起来一个是前端交互的事,一个是后端安全策略的事,但实际动手改的时候,它们就像一对连体婴——你只改前端或者只改后端,后面必定踩坑。这篇分享会把两条主线彻底拆开讲清楚,从验证码的生成校验链路,到会话超时的底层配置,再到改完之后的坑位清单,按我的实操顺序来写,照着抄就行。

适合谁看?正在做若依二开、需要把默认登录流程改造成自己项目形态的朋友,尤其是团队里没人接手过安全配置、全靠自己摸索的那批人。我会把项目里前后端分离部署的场景也覆盖进去,不会只讲单机玩法。

1. 先说清楚:验证码和超时登录背后,是三层咬合的链路

1.1 若依的验证码,不是一个简单的“输入框校验”

很多人上来就删页面上的验证码输入框,删完一登录就报“验证码失效”或者“验证码错误”,然后开始怀疑后端是不是有缓存。这其实就是没有看穿验证码整条链路的缘故。

若依的验证码功能,横跨前端、后端、缓存三层,缺一不可。我自己梳理的调用流程是这样的:

  1. 打开登录页,前端向/captchaImage发起请求,拿到一张 base64 图片和对应的 uuid。
  2. 后端生成随机图形验证码时,把答案存在 Redis 或者 Session 里,以 uuid 作为 key。图片本身不存答案,图片只负责给人看。
  3. 用户在页面上看到图片,输入验证码,点击登录。
  4. 登录请求把 username、password、code、uuid 一起提交给后端。
  5. 后端登录服务先拿 uuid 去查缓存里的答案,和用户输入的 code 做比对,一致才继续走用户名密码认证。

所以,取消验证码至少涉及三处改动:前端登录页不显示验证码输入框、登录请求不携带 code 和 uuid、后端跳过验证码校验。这三处是“至少”,如果漏掉其中任何一处,表现出的现象都是登录不成功。

拿实际报错来对一下:如果你前端还在提交 code,后端把验证码开关关了,一般不报错,因为后端直接不校验。如果你前端不提交 code,后端还在校验,那就必报“验证码错误”或“验证码失效”。所以大多数配置联动的问题,都可以靠检查“请求参数”和“后端校验逻辑”这两个方向定位。

1.2 验证码一线的缓存细节,注释里不会写

这里我要展开讲一个常规文档里看不到的点。若依有些版本把验证码答案存在 Redis,有些版本存在 Session。判断方法很简单:打开config或service里验证码生成的那段代码,看它调的是redisCache.setCacheObject还是session.setAttribute。

这个细节为什么重要?因为如果你依赖 Session 存验证码答案,那么验证码是否有效,和 Session 是否有效直接相关。若依的默认会话超时是 30 分钟,如果用户打开登录页超过半个小时再输入验证码,点击登录就会提示“验证码失效”——其实验证码本身没坏,是承载它的 Session 先过期了。

这个问题在“取消验证码”之后反而更容易暴露,因为很多团队是联动改的:验证码关了,登录顺畅了,但超时时间还沿用默认的 30 分钟,用户写一篇长文档写到一半,回来一提交,提示重新登录。这就引出了第二个话题。

1.3 超时登录的“超时”,到底卡在哪一层

在 Spring Boot + Spring Security 的体系里,“超时”指会话(Session)在设定时间内没有活动,或者达到固定有效期后失效。若依默认配置下,用户 30 分钟不操作,下一次点击就会被判定为未认证,被重定向到登录页。

这个过程涉及两层:

  • 后端层:Session 在容器里失效,或者缓存中的登录用户信息过期。
  • 前端层:接口请求返回 401 或 302,由 axios 拦截器统一处理,跳转到登录页。

为什么一定要说清楚两层?因为我见过太多人只改后端配置,前端拦截不动,导致接口返回 401 后页面卡在“正在加载”状态,既不跳转也不提示。还有的人只改前端跳转,后端 Session 其实早就过期了,用户每次点击都白跳一次登录页,体验更差。

要理解超时登录,可以把它想成一张电影票:后端是检票员,Session 就是票面有效期;前端是引导员,负责在票过期时把观众带回售票处。只改引导员或者只改检票员,都会导致观众迷路。

2. 取消验证码:三种改法,我推荐组合式配置开关

2.1 改法一:后端配置开关 + 前端组件移除,推荐优先做

在若依官方版本中,验证码是否启用,配置里其实预留了一个开关。通常在application.yml里:

ruoyi: # 验证码开关,改 false 后登录不再校验验证码 captchaEnabled: false

这个开关不是摆设,后端登录服务在真正校验验证码之前会判断这个开关。我核实过几个版本,逻辑基本相同,大意是:

String code = loginBody.getCode(); // 前端提交的验证码 String uuid = loginBody.getUuid(); // 前端提交的验证码标识 // 开关关闭时直接跳过验证码校验 if (!captchaEnabled) { // 不再执行 validateCaptcha(code, uuid) }

如果你用的版本里没有这个开关,可以看下面第三种改法,自己补一个。

前端登录页login.vue也要同步调整。默认代码中,验证码部分包含:验证码输入框、验证码图片、点击图片刷新逻辑、表单校验规则、请求参数装配。改动时要注意把login方法里的请求参数列表精简为username、password、rememberMe。

改完之后的效果是:打开登录页,没有验证码输入框,用户输入用户名密码直接登录。整个登录体验是干净的,而且保留了一个好处——运维或者后端同事以后想重新开启验证码,只需要把配置改回true,不需要翻代码。

2.2 改法二:只动前端隐藏组件,适合联调阶段的临时方案

有些团队前后端并行开发,后端验证码校验代码还没关,前端先把页面改了。这种场景下,前端隐藏验证码输入框,登录请求不提交 code/uuid,后端一校验必出错。

这种情况比较适合用临时方案顶一下:前端先带着“假验证码”提交,比如固定传一个code: "1"和uuid: "1",后端校验时能通过就通过。但我不建议长期这样干,因为假验证码本质上和后端校验逻辑是悖论,后面如果有人把后端校验真正关上,前台还在传假参数,反而容易出一堆诡异问题。

我的建议:联调阶段的临时方案,要在代码里留下明显标记,比如在请求参数那里写一行注释“临时假验证码,后端关闭校验后移除”,方便后面处理。

2.3 改法三:核心登录服务里加环境开关,适合旧版改造

如果你手里的项目是基于旧版若依改的,没有现成的captchaEnabled配置,那就直接在登录服务里改。

以常见的SysLoginService为例,改造思路是:在方法入口处加一个由配置中心控制的布尔值,再决定是否校验验证码。

public String login(String username, String password, String code, String uuid) { // 验证码是否开启,由自己维护的配置项控制 boolean captchaEnabled = configService.selectCaptchaEnabled(); if (captchaEnabled) { validateCaptcha(code, uuid); } // 后续走用户名密码认证、缓存用户信息等流程 }

这个方式的优点是掌控力强,缺点是容易造成环境不一致。比如开发环境captchaEnabled=false,测试环境却是true,联调的时候两边对不上,排查半天发现是配置不同。建议所有环境统一从一个配置中心读取,不要每台机器各写各的。

2.4 三种改法放在一起怎么选

方案改动量可回退性适用场景风险点
配置开关+前端组件移除中小高,配置文件改回 true 即恢复标准若依版本,正规开发前端遗漏,请求参数残留
仅前端隐藏小中,后端不配合会报错前后端联调阶段假验证码参数容易留坑
登录服务代码级跳过较大中,需自行维护开关旧版若依,无现成配置多环境配置不一致

我的倾向非常明确:能用配置的,坚决不动代码;非要动代码的,一定要留开关。验证码是安全边界,改动之前先想好怎么回退。

3. 超时登录调优:从改一个配置到掌控全部细节

3.1 默认超时时间到底在哪里配置

在 Spring Boot 中,控制会话超时最直接的配置是server.servlet.session.timeout。若依默认值通常是 30 分钟,对应配置文件中的写法是:

server: servlet: session: timeout: 30m

这里有一个高频踩坑点:单位。Spring Boot 的timeout值不带单位时默认是秒。常见的错误写法是:

timeout: 30

你以为设置的是 30 分钟,实际是 30 秒。用户刚登录进去,喝口水回来一刷新,登录态就没了。

正确写法:

timeout: 30m # 30分钟 timeout: 1h # 1小时 timeout: 3600 # 3600秒,即1小时

建议尽量用带单位的写法,直观且不容易出错。

3.2 光改 yml 不够,还要看 Spring Security 这一段

如果你的若依版本引入了 Spring Security,光改application.yml可能不够,因为 Security 配置里对会话管理还有一层自己的设置。常见配置如下:

http.sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) .invalidSessionUrl("/login")

这里的invalidSessionUrl("/login")表示会话失效后跳转到登录页。如果这个配置缺失,会话过期后用户可能会看到空白页或者报错页,而不是干干净净地回到登录页。

sessionCreationPolicy的值一般保持IF_REQUIRED,意思是“如果需要就创建会话,否则不创建”。如果改成ALWAYS,系统会强制为每个请求创建会话,内存压力会变大,分布式环境下更明显。如果改成STATELESS,则整个应用不再管理 Session,会影响若依的在线用户管理、验证码存储等功能。所以这个配置项不建议随便动。

3.3 会话超时的完整调整流程,我总结成四步

我每次改超时时间,都按这四步走,几乎没翻过车:

第一步,修改会话超时时间。改application.yml里的server.servlet.session.timeout,按业务需要写成30m、60m或者8h,单位一定要带。

第二步,确认 Spring Security 的会话失效跳转。检查SecurityConfig里的invalidSessionUrl。这一步决定超时后用户看到的页面。

第三步,检查 Redis 中用户缓存的有效期。若依很多版本会使用 Redis 缓存用户登录信息,如果 Redis 的过期时间比 Session 短,可能出现“Session 还在,但用户信息已失效”的诡异现象。两个有效期建议保持一致。

第四步,确认前端 401 拦截。打开request.js,找到响应拦截器,确认收到 401 时会清空本地 token、跳转登录页。

四步走完,超时时间调整才算真正生效。只做第一步,后面可能埋雷。

3.4 前端 401 拦截和路由守卫的正确姿势

若依前端request.js的响应拦截器,是超时处理的关键。一个常见的正确实现是:

service.interceptors.response.use( response => { return response.data; }, error => { const status = error.response && error.response.status; if (status === 401) { // 清空本地登录状态 // 跳转登录页 window.location.href = '/login'; } return Promise.reject(error); } );

这里有两个细节容易被忽略:

一是跳转方式。很多人用 vue-router 的router.push('/login'),如果当前组件还在某些需要权限的页面里,路由守卫可能不让你正常跳过去,最终页面卡死。用window.location.href做整页跳转,会更干脆。

二是 409、403 的处理。遇到并发登录被顶下线时,后端可能返回 409 而不是 401。你的拦截器要同样处理,否则用户被踢下线了,看不到任何提示。

3.5 取消验证码之后,并发会话控制值得补上

我个人坚持一个观点:登录门槛可以降低,安全防线不能单点裸奔。取消验证码之后,至少要把“同一账号允许同时登录几个地方”这个策略明确下来。

若依里可以这样配置:

http.sessionManagement() .maximumSessions(1) // 同一个账号最多一个会话 .maxSessionsPreventsLogin(false) // 已登录时,新登录会顶掉旧会话 .expiredSessionStrategy(new SimpleRedirectSessionInformationExpiredStrategy("/login"));

实测下来,maximumSessions(1)对内部管理系统很实用。用户换电脑登录后,旧登录态被顶下线,同时收到登录页跳转,整个流程是闭环的。

这里有一个和“超时登录”相关的坑:当用户会话因为超时失效,又因为并发控制被顶下线,两种状态在日志里都表现为“会话不存在”,排查时要区分清楚。建议在日志中输出一个loginType字段,区分超时失效和顶号下线,能大幅缩短排查时间。

4. 改完之后的常见问题与踩坑实录

4.1 取消验证码后,登录反而报“验证码失效”

这个现象出现的时候,第一步不是看后端,而是打开浏览器的开发者工具,看网络请求的载荷。确认请求里还有没有code和uuid字段。如果还在,说明前端改动没生效,多半是浏览器缓存了旧版login.js。强制刷新一次,或者给静态资源加版本号。

如果请求里已经没有验证码参数,后端还在报“验证码失效”,那就是后端开关没关或者代码里还在强制校验。按前面说的,去确认captchaEnabled配置。

我遇到过一次非常隐蔽的问题:Nginx 对静态文件做了缓存,前端更新的login.vue编译产物没有同步到用户浏览器上,用户反复刷新看到的都是旧页面。后来在 Nginx 配置里对静态资源关闭了强缓存,问题才彻底解决。

4.2 超时时间改了,实际还是 30 分钟就过期

这个问题的排查思路,核心是确认“到底哪个配置被加载了”。

第一步,确认配置文件路径。若依通常有多套配置,比如application.yml、application-dev.yml、application-prod.yml。你改的可能只是application-dev.yml,但运行环境加载的是application-prod.yml。

第二步,搜索是否还有别的地方设置了超时时间。有些项目会在自定义配置类里重新定义SessionRegistry,或者在后端代码里直接session.setMaxInactiveInterval(1800),写死 1800 秒。这种代码优先级比 yml 高,改文件没用。

第三步,验证是否多实例部署。如果有两台实例,只有一台配置生效,用户请求路由到另一台时,用的还是旧配置。多实例部署务必统一配置,用配置中心管理。

我自己的验证方法是:临时把超时时间改成1m,重新登录,等待 1 分钟,刷新页面。如果被踢下线,说明配置生效,再改回目标值。这个快速验证法能多快好省地定位问题。

4.3 会话过期后跳转空白页,而不是登录页

空白页问题的核心原因,几乎都是前端路由守卫没能正确处理过期状态。

若依前端有一个permission.js的路由守卫,在用户刷新页面后会先拉取用户信息,再决定放行还是跳转。如果用户会话过期,拉取用户信息的接口返回 401,但路由守卫里没有针对 401 的跳转逻辑,页面就会一直停在空白状态。

解决方式是把兜底逻辑放到 axios 拦截器里,只要接口返回 401,就统一清本地状态并跳转登录页。不要在路由守卫里依赖某个特定接口的返回,因为路由守卫只在路由跳转时触发一次,而拦截器能覆盖所有请求。

4.4 超时相关配置速查表

现象可能原因解决方案
取消验证码后仍然报验证码错误前端请求残留 code/uuid,或后端开关未生效检查网络请求载荷,确认 captchaEnabled=false
验证码开关改 false 后不生效多环境配置混乱用快速验证定位实际加载的配置文件
超时时间写 30 变成 30 秒单位缺省默认秒写 30m 或 1h
会话过期后页面白屏前端 401 处理缺失在 axios 拦截器统一处理跳转
同一账号可以多个地方同时登录未配置并发会话控制maximumSessions(1)
用户说“没操作也会掉线”Session 有效期和 Redis 有效期不一致两端有效期统一

这张表是我在实际项目里一点点攒出来的,比任何配置文档都管用。

4.5 还有一个不该忘的:静态资源缓存和页面刷新

最后补一个很多人想不到的坑。如果前端部署时给登录页、静态 JS 都配置了长缓存,用户第一次打开页面用的是旧版本。你辛辛苦苦把验证码取消了,用户那边看到的还是带验证码的老页面。

处理办法是在 Nginx 里对index.html配置Cache-Control: no-cache,动静分离,弹性策略。项目上线后,让用户强制刷新一次,或者配合版本号解决。

改动完成后,我自己最大的感受是:验证码和超时,本质上都是登录态的边界管理。把验证码关掉,相当于把第一道门打开;超时时间拉长,相当于延长了这道门的开放时间。门开得更久,就更要在里面做好房间隔离,也就是会话并发控制、401 兜底处理这些事。

最后分享一个我坚持了很久的小习惯:每次改完这类登录相关配置,都用一分钟超时快速验证一遍,确认机制真正生效了,再改回目标值。测一次只需要一分钟,但能在版本上线前把问题都拦下来,省下的可不止一小时。

返回列表