ruoyi-vue前后端分离架构下的统一登录认证实践指南
1. 理解ruoyi-vue的统一登录认证场景当你需要把ruoyi-vue作为子系统集成到企业统一认证平台时传统的独立登录方式就行不通了。想象一下这样的场景员工在OA系统登录后点击财务系统链接时不应该再输密码这就是典型的单点登录需求。在ruoyi-vue中实现这种统一认证核心要解决三个关键问题首先是票据验证机制。就像电影院检票员要检查你的票根一样系统需要验证从统一平台带来的ticket参数是否合法。这个ticket相当于临时通行证通常由认证中心颁发包含用户身份信息和有效期等数据。我遇到过最坑的情况是某些厂商的ticket居然区分大小写调试了半天才发现是大小写敏感问题。其次是会话状态同步。好比你在商场不同店铺消费时收银员都能查到你的会员状态。我们需要定时检查用户在统一平台的登录状态是否仍然有效通常采用心跳检测机制。这里有个性能优化点不宜设置太短的检测间隔我建议控制在5-10分钟为宜。最后是失败处理策略。就像地铁闸机检测到无效票会提示请重新购票一样当票据验证失败或会话过期时应该优雅地跳转回统一登录页。这里要特别注意避免循环跳转曾经有个项目因为重定向逻辑没处理好导致浏览器死循环跳转把CPU跑满了。2. 前端改造实战步骤2.1 创建专用登录组件在/src/views/下新建login_sso.vue文件这个组件相当于我们系统的VIP通道。核心逻辑集中在created钩子函数中页面加载时自动触发认证流程。有个细节容易忽略需要处理URL中可能存在的redirect参数保留用户原始访问意图。created() { // 保存可能的原始访问路径 this.redirect this.$route.query.redirect this.loginSso() }票据验证环节要特别注意异常处理。有次线上故障就是因为没捕获网络异常导致页面卡死在加载状态。建议采用以下健壮性写法if (!ticket) { this.$message.warning(缺失认证票据) this.redirectToSso() return } try { await this.$store.dispatch(LoginSso, { ticket }) const res await checkTicket({ ticket }) if (res.code ! 200) throw new Error(res.msg) sessionStorage.setItem(ticket, ticket) this.$router.push(this.redirect || /) } catch (err) { console.error(认证失败:, err) this.redirectToSso() }2.2 路由与权限配置在permission.js中需要把sso登录页加入白名单否则会被路由守卫拦截。我习惯用正则匹配路由路径const whiteList [ /login, /login_sso, /^\/sso\/./ // 匹配所有/sso开头的路径 ]在index.js中添加路由配置时建议设置meta信息标记这是SSO路由{ path: /login_sso, component: () import(/views/login_sso), meta: { isSso: true } }3. 后端关键改造点3.1 票据验证控制器创建LoginSsoController时要注意三个安全要点验证票据有效性、检查用户映射关系、生成本地会话。这里分享一个实际项目中的验证逻辑PostMapping(/loginSso) public AjaxResult loginSso(String ticket) { // 1. 基础校验 if (StringUtils.isEmpty(ticket)) { return AjaxResult.error(票据不能为空); } // 2. 调用认证中心验证 JSONObject ssoResult ssoClient.validateTicket(ticket); if (!ssoResult.getBoolean(valid)) { return AjaxResult.error(无效票据); } // 3. 用户映射检查 String ssoUser ssoResult.getString(username); SysUser user userService.selectUserBySsoAccount(ssoUser); if (user null) { return AjaxResult.error(用户未同步); } // 4. 创建本地会话 String token loginService.loginNoCaptcha(user.getUserName(), defaultPwd); return AjaxResult.success().put(token, token); }3.2 无验证码登录方法在SysLoginService中扩展的loginNoCaptcha方法本质是跳过图形验证码的标准登录流程。注意这里要复用已有的认证逻辑public String loginNoCaptcha(String username, String password) { // 使用Spring Security标准认证流程 Authentication authentication authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(username, password)); // 生成JWT令牌 LoginUser loginUser (LoginUser) authentication.getPrincipal(); return tokenService.createToken(loginUser); }有个性能优化技巧可以在这里添加登录日志的异步记录避免阻塞主流程AsyncManager.me().execute(AsyncFactory.recordLogininfor( username, Constants.LOGIN_SUCCESS, MessageUtils.message(user.login.success)) );4. 安全增强与异常处理4.1 防御性编程实践在票据验证环节要特别注意三点防御措施设置合理的HTTP超时建议连接超时3秒读取超时5秒对认证中心返回数据做完整性校验实施请求频率限制private JSONObject validateTicket(String ticket) { // 1. 频率检查 if (rateLimiter.tryAcquire()) { throw new BusinessException(请求过于频繁); } // 2. 带超时的HTTP调用 HttpRequest request HttpRequest.get(ssoUrl) .connectTimeout(3000) .readTimeout(5000) .query(ticket, ticket); // 3. 响应验证 JSONObject result JSON.parseObject(request.body()); if (!result.containsKey(signature)) { throw new BusinessException(响应签名缺失); } if (!verifySignature(result)) { throw new BusinessException(签名验证失败); } return result; }4.2 会话状态校验优化定时校验不宜直接调用远程接口可以采用本地缓存异步刷新的策略。这里给出个折中方案// 前端实现 setInterval(async () { const ticket sessionStorage.getItem(ticket) if (!ticket) return try { const res await checkTicket({ ticket }) if (res.code ! 200) { alert(会话已过期) redirectToSso() } } catch (err) { console.warn(状态检查异常:, err) // 网络异常时不强制退出 } }, 300000) // 5分钟检查一次后端对应的检查接口应该轻量化避免复杂业务逻辑PostMapping(/checkTicket) public AjaxResult checkTicket(String ticket) { // 只做基础格式验证 if (StringUtils.isBlank(ticket)) { return AjaxResult.error(票据为空); } return AjaxResult.success(状态正常); }5. 部署与调试技巧5.1 跨域问题解决方案在前后端分离架构下可能会遇到CORS问题。建议在Nginx配置中添加location / { add_header Access-Control-Allow-Origin $http_origin; add_header Access-Control-Allow-Credentials true; add_header Access-Control-Allow-Methods GET,POST,OPTIONS; add_header Access-Control-Allow-Headers Content-Type,Authorization; if ($request_method OPTIONS) { return 204; } }5.2 日志排查指南调试阶段建议开启详细日志重点关注三个地方票据验证请求和响应用户映射查找过程令牌生成环节在logback配置中添加logger namecom.ruoyi.web.controller.system.LoginSsoController levelDEBUG/ logger namecom.ruoyi.framework.web.service.SysLoginService levelDEBUG/遇到认证失败时可以按照这个检查清单排查检查ticket是否正常传递验证网络连通性检查时间戳是否同步确认用户映射关系存在检查JWT密钥配置在Chrome开发者工具中建议重点关注Network和Console两个面板。有个实用技巧在请求头中添加特殊标记方便日志追踪axios.interceptors.request.use(config { config.headers[X-Trace-Id] generateTraceId() return config })