资讯动态

若依框架对接微信小程序登录:openid绑定与Token鉴权全攻略

发布时间:2026/10/17 8:21:51 来源:尧图企业网站定制
接手这个需求的第一反应就是后台是若依那套成熟的账号密码登录小程序端连个密码框都没有用户点一下微信授权就要晃进来两套完全不同的认证体系怎么接标题里那句“登录解决”看着简单真正动手才发现要过数据库设计、接口开发、权限放行、联调适配一大圈。这篇文章把我折腾了一周多、翻了不少底层源码之后的完整方案整理出来从需求梳理到落库到鉴权到上线适配一条线讲透。1. 两套登录体系为什么对不上先弄清各自做了什么很多人一上来就想着改若依的登录接口结果越改越乱。原因很简单若依后台登录和微信小程序登录根本是两种认证模型硬套只会把原有体系搞坏。1.1 若依后台登录链路回顾若依管理系统采用 Spring Security JWT 的方案登录流程大致是前端提交账号密码触发SysLoginService.login()。校验验证码然后AuthenticationManager执行认证内部会调用UserDetailsServiceImpl按用户名加载用户。加载到用户后比对密码成功则构造LoginUser对象调用TokenService.createToken()生成一个 UUID 格式的 token。token 存到 RedisKey 通常是login_tokens:uuidValue 是序列化后的LoginUser。前端拿到 token 后后续请求在 Header 里带Authorization: Bearer token。每次请求进来JwtAuthenticationTokenFilter会从请求头解析 token再去 Redis 拿LoginUser放进SecurityContextHolder之后 Spring Security 就能识别当前登录用户。这套链路里用户身份靠的是“账号 密码”这个入口所有鉴权都围绕LoginUser展开。1.2 微信小程序登录链路回顾小程序侧的登录完全不是一回事。用户在授权弹窗点一下前端调用wx.login()拿到一个短期有效的code把code传给后端。后端用code去微信服务器换openid和session_key换完以后code就废了。这个过程中后端拿到的核心身份凭证是openid这个用户在某个小程序 AppID 下的唯一标识。注意它和账号密码没有任何关系它天然就是为“无密码登录”设计的。1.3 对接的破局点用 openid 当账号两套体系能对接的核心思路就是让openid和若依的sys_user表产生关联。小程序用户第一次进来时后台自动帮他注册一个系统账号并记录 openid 与该账号的绑定关系之后再进来直接根据 openid 找到账号走自定义的登录逻辑生成 token。这样原有的后台账号密码登录完全不用动小程序走独立接口两边井水不犯河水。想明白这一点后面的改造就顺了。2. 数据库设计一个绑定表解决多个关联问题设计数据表是这个需求里最容易被低估的部分。很多人图省事直接在sys_user表上加一个openid字段短期看确实简单长期看隐患不少。2.1 直接给 sys_user 加 openid 为什么不推荐openid不是一个全局统一的标识。同一个用户在不同的微信小程序 AppID 下的 openid 是不一样的如果后续还可能会用到公众号、企业微信那又有另一套 openid 或 unionid。把 openid 直接放在sys_user上意味着这个字段的语义被限死了这个用户只能绑定一个小程序。另一个问题是sys_user是若依的核心用户表角色、部门、岗位全部围绕它打转。贸然加字段后续写通用代码、维护数据字典、做多租户扩展都会变成一场灾难。2.2 新增绑定表的完整结构与说明更合理的做法是新建一张独立的绑定表用户和微信身份是多对多关系。一张表同时支持多 AppID、多端、后续扩展公众号也不慌。-- 微信小程序用户绑定表 CREATE TABLE sys_user_wxapp ( id BIGINT AUTO_INCREMENT COMMENT 主键ID, user_id BIGINT NOT NULL COMMENT 关联系统用户ID, openid VARCHAR(128) NOT NULL COMMENT 微信小程序用户标识, app_id VARCHAR(64) NOT NULL COMMENT 小程序AppID, union_id VARCHAR(128) DEFAULT COMMENT 开放平台统一标识有则存, session_key VARCHAR(64) DEFAULT COMMENT 微信会话密钥注意加密存储, bind_time DATETIME DEFAULT NULL COMMENT 首次绑定时间, last_login_time DATETIME DEFAULT NULL COMMENT 最近一次登录时间, PRIMARY KEY (id), UNIQUE KEY uk_openid_appid (openid, app_id), KEY idx_user_id (user_id) ) ENGINEInnoDB COMMENT系统用户与微信小程序绑定关系表;这个表结构里有几个关键点唯一索引落在(openid, app_id)上保证同一 AppID 下的同一 openid 只有一条记录。session_key一定不能返回给前端它只在后端用来解密敏感数据比如手机号。落库时最好加密存储避免数据库泄露直接连 session_key 一起丢。预留union_id字段等接入微信开放平台后可以把多个小程序、公众号的身份统一起来。2.3 自动注册账号的边界情况处理openid 首次访问时系统需要自动注册一个若依用户。这个自动注册逻辑建议用事务包住避免出现“用户建了但绑定没成功”的中间状态。用户名生成要考虑唯一性不能和后台已有账号撞车。我的做法是String userName wx_ UUID.randomUUID().toString().replace(-, ).substring(0, 16);密码则随机生成一串然后用若依已有的加密工具加密后落库。这样能防止这个账号被密码爆破也避免出现“空密码”这种低级问题。用户状态直接置为正常status 0逻辑删除字段置为未删除。这里要注意若依 4.x 用del_flag5.x 是deleted写代码时按自己项目的实体字段来别照抄。3. 后端接口落地接住 code换回 openid再产出 token数据层面想清楚接下来就是接口。小程序端只需要一个登录接口后端要做的事不少转发请求到微信服务器、解析结果、查询或创建用户、生成 token。3.1 请求微信接口换取会话凭证微信官方接口是固定格式用 GET 请求把appid、secret、js_code、grant_type传过去https://api.weixin.qq.com/sns/jscode2session ?appidAPPID secretSECRET js_codeCODE grant_typeauthorization_code返回结果有两种情况。成功时返回 openid 和 session_key失败时返回 errcode 和 errmsg。HTTP 客户端的选择上项目里已经用了若依自带的HttpUtils就直接用也可以引入 Hutool 的HttpUtil本质上是同一个操作。重点在于这个请求必须由后端发起secret绝对不能出现在小程序前端代码里。3.2 封装 WxLoginController 与 Service后端接口拆两层Controller 只做参数接收和结果返回具体逻辑放在 Service 里方便后续单元测试和复用。RestController RequestMapping(/wxapp) public class WxAppLoginController { Autowired private WxAppLoginService wxAppLoginService; PostMapping(/login) public AjaxResult login(RequestBody WxLoginBody body) { if (StringUtils.isBlank(body.getCode())) { return AjaxResult.error(code不能为空); } return wxAppLoginService.login(body.getCode(), body.getAppId()); } }Service 层的逻辑按四步走Override public AjaxResult login(String code, String appId) { // 1. 请求微信接口用 code 换取 openid 和 session_key WxSessionResponse wxResp wxApiClient.jscode2session(code, appId); if (wxResp.getErrcode() ! null) { throw new ServiceException(微信登录失败: wxResp.getErrmsg()); } // 2. 根据 openid appId 查绑定记录 SysUserWxapp bind wxAppUserMapper.selectByOpenidAndAppId( wxResp.getOpenid(), appId); SysUser sysUser; if (bind null) { // 3. 首次登录自动注册系统账号并建立绑定 sysUser wxAppLoginService.registerAndBind(wxResp.getOpenid(), wxResp.getUnionid(), appId, wxResp.getSessionKey()); } else { sysUser sysUserService.selectUserById(bind.getUserId()); if (sysUser null || 1.equals(sysUser.getStatus())) { throw new ServiceException(账号不存在或已被停用); } // 更新最近登录时间 wxAppUserMapper.updateLastLoginTime(bind.getId()); } // 4. 构造 LoginUser生成若依 token 返回 MapString, Object tokenMap tokenService.createToken(buildLoginUser(sysUser)); return AjaxResult.success(登录成功, tokenMap); }自动注册和绑定要特别注意事务我对这个方法的处理是加Transactional确保两张表的数据要么都成功要么都回滚。3.3 复用若依 TokenService 为小程序用户生成登录态这是整个方案最妙的一步不需要自己发明 token 机制直接复用若依的TokenService。private LoginUser buildLoginUser(SysUser sysUser) { LoginUser loginUser new LoginUser(); loginUser.setUserId(sysUser.getUserId()); loginUser.setUsername(sysUser.getUserName()); // 允许访问基础接口 loginUser.setPermissions(getWxAppPermissions()); // 如果有部门、岗位等信息也可以一并填充 return loginUser; }TokenService.createToken(loginUser)会帮我们完成 token 生成、Redis 存储和过期处理。后续所有请求照样走JwtAuthenticationTokenFilter和后台登录用户没有区别。真正需要想清楚的是权限集合。若依的LoginUser里存的是权限标识列表后台用户的权限从角色菜单里刷出来小程序用户没有角色就可能是空。空权限会直接导致访问PreAuthorize(hasPermission(...))标注的接口时被拒绝。我的做法是给小程序用户分配一个默认角色或者调整需求小程序专用接口不强制要求细粒度权限构造 LoginUser 时给一个全集或常用集合。4. 与 Spring Security 的兼容处理放行、鉴权、过滤器链后端接口写好后不配置 Spring Security 是跑不起来的因为默认所有请求都要先过认证。这一步改的是若依的SecurityConfig。4.1 匿名放行小程序登录接口若依默认的放行列表里有/login、/register、验证码等地址。小程序登录接口要在登录之前就能被访问所以必须加进放行列表。.antMatchers(/wxapp/login).anonymous()如果用的是若依 5.x 这类 Spring Security 6.x 版本方法名可能是requestMatchers替换成对应写法即可。千万不要用permitAll()这个区别我在后面踩坑部分细说。4.2 自定义登录后如何让 JWT 过滤器认账接口放行之后还有个隐性问题小程序登录接口返回的 token 怎么被JwtAuthenticationTokenFilter识别。若依的过滤器逻辑很直接从请求头Authorization里取Bearer xxx去掉前缀拿到 token再从 Redis 的login_tokens:xxx里反序列化出LoginUser。因为我们用的是同一个TokenServicetoken 格式和存 Redis 的 Key 完全一致过滤器不需要任何改动直接认账。也就是说前端拿到 token 后只要在后续请求的 Header 里带上Authorization: Bearer 522f8d9a6f3b4c5e8d5e9f7a2b3c4d5e就一切正常。这块核心就是不重复造轮子前后台共用一套 token 体系。4.3 权限不足与 401 的排查思路联调时常见两个状态码401 和 403。401 是“没有认证信息或 token 无效”先看 Header 有没有带再看 token 前缀是不是Bearer最后看 Redis 里 token 还在不在。这几个检查基本能覆盖 90% 的情况。403 是“认证了但没权限”这时候要查LoginUser里的permissions集合。我建议一开始就给小程序用户分配默认角色然后把sys_user_role的关联关系建立好这样权限问题可以顺着已有角色链路排查而不是绕开权限体系直接给全集。5. 联调过程中的真实踩坑记录方案跑通并不等于万事大吉联调阶段才是真正让人头秃的开始。下面这几个坑我基本都踩过写出来给大家省点时间。5.1 code 换不到 openid 的几种情形第一次联调时后端拿 code 去请求微信接口返回errcode: 40029当时一脸懵。后来排查发现是js_code参数被拼错了拼成了codexxx。微信要求参数名必须是js_code一个字母都不能错。还有两种情况很隐蔽同一个 code 被重复使用了。wx.login()返回的 code 只能使用一次前端如果因为网络抖动自动重试第二次必然失败。前端要做好防重复提交。AppID 和密钥不匹配。开发版、体验版、正式版如果切换了 AppID后端的secret没跟着换也会报40125之类的错误。我处理这个问题的招数是后端把微信返回的errcode和errmsg原样记下日志排查时直接看日志定位比反复问前端“你再试试”高效得多。5.2 小程序不是“跨域”是域名白名单问题很多有 Web 开发经验的人遇到小程序请求后端不通第一反应是“跨域”。实际在小程序环境下wx.request不受浏览器同源策略限制根本不存在传统意义的跨域报错。真正拦路的是域名白名单。开发者工具一打开控制台大概率看到这句不在以下 request 合法域名列表中请参考文档处理方式有两种。开发阶段在微信开发者工具右上角点击“详情 - 本地设置”勾选“不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书”。上线阶段在微信公众平台把后端接口域名配置到“开发管理 - 开发设置 - 服务器域名 - request 合法域名”里而且要全站 HTTPS证书有效期注意别过期。5.3 前后端联调时最容易忽略的 token 头格式好不容易接口通了token 也返回了结果访问业务接口还是 401。排查半天发现前端把 token 直接放在 Header 里没加Bearer前缀。这种问题在开发者工具里特别容易发生因为复制粘贴的时候只复制了那串 UUID漏了前缀。建议前端把设置 Header 的逻辑封装成统一方法所有请求共用const token wx.getStorageSync(token) if (token) { header[Authorization] Bearer token }我在项目里还额外加了一层判断token 为空时直接跳转登录页避免业务接口全部 401 之后才被动处理。5.4 真机调试不生效的坑开发者工具上跑得好好一上真机就登录失败。这个坑多数情况是 AppID 不一致开发者工具用的测试号真机用的正式 AppID而后端配置还是旧的那套。我的建议是从联调第一天开始所有环境统一使用同一个 AppID上线前再切正式并且后端配置中心区分dev和prod两套参数避免多人开发时互相覆盖配置。6. 上线前必须处理的版本适配细节登录能通只是第一步小程序这边还有一些平台规则层面的细节不提前处理审核和用户体验都会出问题。6.1 头像昵称接口调整后的对策早期小程序可以用wx.getUserInfo或者wx.getUserProfile直接拿用户头像昵称。平台规则调整之后这两个接口拿到的昵称统一变成“微信用户”头像变成默认灰色头像显示出来很难看。现在官方推荐的是“头像昵称填写能力”头像用buttonopen-typechooseAvatar让用户主动选择图片。昵称用input的typenickname引导用户填写。后端拿到这些信息后再更新sys_user的nick_name和avatar字段。如果用户没主动设置就保持默认值不要依赖微信直接给真实头像昵称了。button open-typechooseAvatar bind:chooseavataronChooseAvatar image src{{avatarUrl}} modeaspectFill/image /button input typenickname placeholder请输入昵称 bindchangeonNickNameChange /6.2 手机号授权登录怎么接很多管理系统的业务是强实名制光有 openid 不够需要绑定手机号。小程序的手机号授权和登录 code 不是一回事。需要在小程序里放一个带open-typegetPhoneNumber的按钮用户点击授权后回调事件里会返回一个code。把这个code传给后端后端调用微信的https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_tokenACCESS_TOKEN请求体传{code: 回调里拿到的code}就能换到用户手机号。注意这里的access_token是接口调用凭证和小程序的session_key不是同一个东西需要后端单独走一遍cgi-bin/token接口获取而且时效只有 7200 秒最好做个缓存。拿到手机号之后可以先查sys_user里该手机号有没有绑定已有账号如果有直接建立微信身份与账号的绑定如果没有就注册一个新账号并绑定手机号。这样就把小程序登录和系统已有用户很好地融合在一起了。6.3 小程序端环境区分与配置建议上线前最后一件大事是环境配置。小程序不像 Web 那样直接区分 dev、test、prod但可以在代码里预留配置入口const ENV { dev: { baseUrl: https://dev-api.example.com/prod-api, appId: wx_dev_appid }, prod: { baseUrl: https://api.example.com/prod-api, appId: wx_prod_appid } } const currentEnv dev切换环境时只要改一行变量。后端也要在配置文件里对应维护两套微信凭证不能让 AppID 和 Secret 散落在代码各处。我个人在实际项目里还会把“小程序登录”独立成一个通用模块保存下来换项目时直接复制表结构、Service、Controller只改安全和域名配置十分钟就能再接一个新系统。下次再做微信小程序对接若依不用再从零查一遍资料了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑