若依鉴权迁移实操指南:把 Cloud 版登录移植到单体版模板

若依鉴权迁移实操指南:把 Cloud 版登录移植到单体版模板
本文记录一次完整的前端鉴权迁移过程把一套若依 Cloud微服务版项目的登录与鉴权机制整体移植到一个若依单体版 Vue3 模板里做到用原有账号直接登录新模板且登录状态行为与原项目一致。全程只动前端不碰后端、接口地址、数据库。一、先搞清楚为什么要迁迁什么若依有两套主流前端若依单体版RuoYi-Vue3若依 Cloud 版RuoYi-Cloud登录接口POST /login明文 JSONPOST /auth/loginAES 加密的密文密码保护无或仅前端 RSA 记密码RSA AES 双重加密用户信息GET /getInfoGET /system/user/getInfo菜单GET /getRoutersGET /system/menu/getRouters多系统区分无靠请求头X-AppNo区分子系统token 返回字段res.tokenres.data.access_token我们要做的事让单体版模板的登录流程完全按 Cloud 版的加密方式、接口路径、请求头去和后端对话。如果只是改个接口地址登录一定失败——因为 Cloud 版后端期待的是加密后的请求体和加密后的请求头少一个都解不出来会直接报 500。二、迁移前要准备的两个东西源项目那个已经能正常登录的若依 Cloud 版前端我们要从它身上拆逻辑。目标项目要接入登录的若依单体版 Vue3 模板。两套代码都放在本地方便随时对照复制。下文用源项目/和目标项目/指代。三、迁移分几步整体 6 步按顺序做加密工具AES RSA接口层登录、用户、菜单、退出请求拦截器带 token、带 appNo用户状态管理store登录页面路由守卫一般模板已有检查即可下面逐步说明每一步都会给出从源项目复制什么、在目标项目改什么。四、第一步加密工具Cloud 版登录的加密方案是这样的随机生成 16 位 AES 密钥 key ├─ 用 key 把 {用户名,密码,验证码,uuid} AES 加密 → 作为请求体 └─ 用 RSA 公钥把 key 加密 → 放进请求头 X-Cipher 后端用 RSA 私钥解出 key再用 key 解出请求体所以我们需要两个工具文件。4.1 新建目标项目/src/utils/cryptojs.js直接从源项目/src/utils/cryptojs.js整个复制过来。里面包含AESEnc(key, content)—— AES-CBC 加密AESDec(key, content)—— AES-CBC 解密aseKey()—— 生成 16 位随机密钥注意里面的固定加密向量iv必须和源项目一字不差否则后端解不出。4.2 替换目标项目/src/utils/jsencrypt.js这一步最关键是登录能否成功的核心。打开两个项目的src/utils/jsencrypt.js把源项目里的publicKey和privateKey两个常量原样覆盖到目标项目。为什么要换因为 RSA 是非对称加密前端用公钥加密 key后端用配对的私钥解密。若依单体版模板自带的那对密钥和 Cloud 版后端手里的私钥不是一对。公钥不匹配 → 后端解密失败 → 报 500。判断方法源项目能登录说明源项目的公钥和后端私钥是配对的。把这个公钥搬过来就对了。4.3 安装依赖cryptojs.js依赖crypto-js这个库目标模板默认没有要装pnpmaddcrypto-js然后在package.json的dependencies里会自动出现crypto-js。五、第二步接口层把登录相关的接口地址从单体版改成 Cloud 版。涉及两个文件。5.1 改目标项目/src/api/login.js对照源项目的同名文件把接口路径和加密逻辑搬过来。核心是login函数importrequestfrom/utils/requestimport{encrypt}from/utils/jsencryptimport{AESEnc}from/utils/cryptojs// 登录方法exportfunctionlogin(username,password,code,uuid,key){returnrequest({url:/auth/login,headers:{isToken:false,repeatSubmit:false,X-Cipher:encrypt(key)// RSA 加密后的 key放进请求头},method:post,data:AESEnc(key,JSON.stringify({username,password,code,uuid}))// ↑ 用 AES 加密后的登录信息作为请求体})}其余接口也要改成 Cloud 版路径函数单体版路径Cloud 版路径getInfo/getInfo/system/user/getInfologoutPOST /logoutDELETE /auth/logoutgetCodeImg验证码/captchaImage/code经验直接把源项目的login.js整个搬过来最省事再按需保留目标模板里特有的接口比如单体版可能有unlockScreen解锁屏留着不冲突。5.2 改目标项目/src/api/menu.js菜单接口加/system前缀exportconstgetRouters(){returnrequest({url:/system/menu/getRouters,method:get})}六、第三步请求拦截器request.js打开目标项目/src/utils/request.js做两处改动。6.1 每个请求带X-AppNo头Cloud 版靠这个头区分这是哪个子系统在请求后端用它筛选菜单、有时还会校验登录合法性。importdefaultSettingsfrom/settings// 文件顶部加上这行 importservice.interceptors.request.use(config{// 加上这一行子系统标识config.headers[X-AppNo]defaultSettings.appNo// ...原有的 token、防重复提交逻辑保持不变})6.2 在settings.js里配 appNoexportdefault{appNo:你的子系统标识,// 必须是后端已注册的标识否则可能登录 500// ...}重要提醒appNo的值必须是后端运维那里已经注册过的子系统标识。如果随手编一个后端不存在的值登录可能直接报 500后端找不到这个子系统配置。拿不准时先用源项目里那个能登录的 appNo。6.3 请求超时Cloud 版网关响应有时偏慢建议把超时从默认的 10 秒调到 60 秒constserviceaxios.create({baseURL:import.meta.env.VITE_APP_BASE_API,timeout:60000// 单体版默认 10000调大避免误超时})七、第四步用户状态管理store/modules/user.js这是登录的核心逻辑要改两个 action。7.1login—— 接收密钥、取 access_token单体版 store 的 login 长这样login(userInfo){constusernameuserInfo.username.trim()// ...login(username,password,code,uuid).then(res{setToken(res.token)// ← 单体版取 res.token})}Cloud 版要改成login(userInfo){constkeyuserInfo.aseKey// ← 接收前端生成的随机密钥constusernameuserInfo.username.trim()// ...login(username,password,code,uuid,key).then(res{letdatares.datasetToken(data.access_token)// ← Cloud 版取 res.data.access_tokenthis.tokendata.access_token})}两处关键差异多传一个key参数AES 密钥由登录页生成token 从res.token改成res.data.access_token7.2getInfo—— 补充业务字段Cloud 版的用户信息接口会返回一些额外字段比如部门、区划编码按需补到 state 里this.regionCodeuser.dept?user.dept.cityCode:null其余逻辑头像处理、角色权限、密码过期提示等保留目标模板原有的即可不冲突。八、第五步登录页面views/login.vue登录页要做三件事生成随机密钥、加密提交、记住密码也用 AES。8.1 引入加密工具import{aseKey,AESEnc,AESDec}from/utils/cryptojs8.2 登录表单加一个aseKey字段constloginFormref({username:,password:,code:,uuid:,rememberMe:false,aseKey:// ← 加这个})8.3 提交前生成密钥functionhandleLogin(){constkeyaseKey()// 每次登录生成新的随机密钥loginForm.value.aseKeykey// ...校验、记住密码、调用 userStore.login(loginForm.value)}8.4 记住密码用 AES 加密// 记住Cookies.set(username,loginForm.value.username,{expires:30})Cookies.set(password,AESEnc(key,loginForm.value.password),{expires:30})Cookies.set(aseKey,key,{expires:30})// 回填时解密constkeyCookies.get(aseKey)loginForm.value.passwordAESDec(key,Cookies.get(password))这里有个容易踩的坑从 Vue2 项目抄来的登录页代码里面可能残留this.loginForm.xxx的写法。在script setup里this是undefined运行会报错。一定要全部改成loginForm.value.xxx。九、第六步路由守卫permission.js通常不用改若依模板自带的permission.js已经实现了完整的登录拦截逻辑没有 token → 跳/login?redirectxxx有 token 但还没拉用户信息 → 调getInfo() 生成动态路由在白名单内的页面登录、注册→ 直接放行这套逻辑 Cloud 版和单体版通用一般不用动。只需要确认一点退出登录后跳哪里单体版模板默认退出后跳/index因为 token 已清守卫检测无 token 会再跳/login行为是对的。如果源项目是跳 SSO 门户而你不需要 SSO就保持模板默认跳/login即可。十、网络代理配置容易漏的一步登录请求要能到达后端vite.config.js的代理 target 必须指向正确的后端网关地址。若依单体版模板默认写死了constbaseUrlhttp://localhost:8080// ← 单体版后端proxy:{/dev-api:{target:baseUrl,...}}如果你的后端是 Cloud 版网关比如https://xxx/gateway这里必须改否则请求会打到本机不存在的服务报 500。推荐做法从环境变量读和源项目保持一致const{VITE_APP_BASE_API,VITE_APP_BASE_API_PROXY}envproxy:{[VITE_APP_BASE_API]:{target:VITE_APP_BASE_API_PROXY,// 从 .env 读网关地址secure:false,// 网关是 https 可能自签证书绕过校验changeOrigin:true,rewrite:(p)p.replace(newRegExp(^VITE_APP_BASE_API),)}}然后在.env.development里配VITE_APP_BASE_API /dev-api VITE_APP_BASE_API_PROXY https://你的后端网关地址改完vite.config.js必须重启 dev 服务热更新对配置文件不生效。十一、验证清单逐项确认cryptojs.js已创建crypto-js已安装jsencrypt.js的公私钥已替换为源项目的login.js接口路径是/auth/login请求体 AES 加密头带X-Ciphermenu.js是/system/menu/getRoutersrequest.js带了X-AppNo头超时 60ssettings.js的appNo是后端已注册的值user.js的 login 取access_token、接收aseKeylogin.vue生成随机 aseKey、无this.残留vite.config.js代理指向正确的后端网关重启了 dev 服务启动后用原账号登录能进首页就说明鉴权链路通了。十二、踩坑实录重点迁移过程中遇到的真实问题按出现顺序记录。坑 1登录报 500「内部服务错误」不管输什么都报现象请求能发出去有 requestId但后端返回 500连账号密码错误都不是。排查思路先确认 RSA 加密是否成功在 login 函数里console.log(encrypt(key))如果是false说明公钥有问题。确认appNo是后端已注册的值先用源项目的 appNo 试。如果以上都对看下一条——axios 版本。坑 2axios 版本不一致导致请求体被二次处理真正的元凶现象密钥、网关、接口、拦截器逻辑全部和源项目一致源项目能登录目标模板就是 500。根因源项目用的是axios 0.27.x若依 Vue3 模板默认用的是axios 1.x两个大版本对字符串请求体AES 加密后 data 是字符串的序列化处理有差异导致发出去的 Content-Type 或 body 格式变了后端解密失败。解决办法把目标模板的 axios 降到和源项目一致的版本。pnpmaddaxios0.27.2改完package.json重装即可登录立刻通。教训迁移鉴权这类对请求格式极其敏感的功能时依赖库的大版本一定要和已验证可用的源项目对齐。axios 0.x 和 1.x 不是简单的小版本升级请求/响应处理行为有变化。这一条很容易被忽略因为它不报错、只让请求体悄悄变了样。坑 3Vue2 抄来的登录页用了this.现象登录页一加载就报Cannot read property xxx of undefined。根因从 Vue2 项目复制的代码里残留this.loginForm.username在 Vue3script setup里this是 undefined。解决全部替换成loginForm.value.xxx。坑 4改了 vite.config.js 没重启现象改完代理配置刷新页面还是报错。解决vite.config.js的改动必须重启 dev 服务CtrlC后重新pnpm devHMR 对配置文件不生效。十三、迁移后的文件改动汇总供对照检查以下是本次迁移涉及的所有文件新增src/utils/cryptojs.jssrc/api/resource.js首页用与鉴权无关src/api/system/app.js首页用与鉴权无关改写src/utils/jsencrypt.js换密钥src/api/login.jssrc/api/menu.jssrc/utils/request.jssrc/utils/auth.jsTokenKey 改名避免同域 Cookie 冲突src/store/modules/user.jssrc/views/login.vuesrc/settings.js加 appNovite.config.js代理.env.*网关地址、标题package.json加 crypto-js降 axios十四、写给下次做这件事的自己如果以后还要做一次类似的迁移记住三件事先对齐依赖版本尤其是 axios。这是最隐蔽的坑。RSA 公钥必须和后端私钥配对直接搬源项目的别用模板自带的。appNo 必须是后端已注册的拿不准就用源项目里那个能登录的。把这三点确认了剩下的就是机械地搬接口路径和加密逻辑基本不会出错。