微信登录
1.1 微信小程序开发
1.1.1 介绍
- 开放注册范围:个人、企业、政府、媒体、其他组织,个人身份注册的小程序是没有支付功能的
- 接入流程:
- 注册
- 小程序信息完善
- 开发小程序
- 提交审核和发布
1.1.2 入门
小程序目录结构


1.2 微信登录流程

小程序调用wx.login()方法,获得一个code,后端拿到该code,再将小程序id和小程序secret以及刚拿到的code一起传给微信的接口服务,微信接口服务就会返回该用户的openid,session_key等。我们将参数返回给小程序,小程序存储在本地,之后的请求都会携带这些参数,我们后端校验后,没问题就放行了,就实现了wxlogin。
请求接口为
textGET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code详见: https://developers.weixin.qq.com/minigame/dev/api-backend/open-api/login/auth.code2Session.html

1.3 开发
业务规则
- 基于微信登录功能实现小程序的登录功能
- 如果是新用户需要自动完成注册
思路
- 通过发送多次请求,发现同一个用户发送的
code返回的session_key和openid都是一样的,为我们存储用户信息提供了思路,是否可以以openid为用户的标识符(,session_key为对应用户的登录秘钥?)- 经过查阅session_key主要用于对用户数据进行加密签名验证和解密,保证前端传递给后端的数据的真实性,以及解密某些使用微信提供的接口获取到的敏感数据,保证接下来的会话是安全的
- session_key使用注意事项:
- 通常将其保存在 Redis 中,并与你颁发给前端的
JWT Token建立映射关系。 session_key是会过期的。当用户频繁使用小程序时,它保持有效;如果用户长时间不用,它会失效。前端可以调用wx.checkSession()来检查它是否过期。如果过期,需要重新走wx.login流程。- 在开发解密手机号的功能时,如果
session_key不匹配,解密会抛出异常。这通常是因为wx.login被重复调用,导致旧的session_key失效,而后端还没更新。
- 通常将其保存在 Redis 中,并与你颁发给前端的
- 还有一个问题,获取用户这些登录信息后,如何将用户的名称和头像等其他信息获取到(有一个思路,如果是新用户,登录成功后,小程序端向后端发送新增用户的请求,将用户的信息发送过去,这里参考到其他小程序的实现)
- 解决方法,暂时不管这些字段,后期由用户去个人中心自行完善
- 通过发送多次请求,发现同一个用户发送的
接口设计
- Post /user/user/login ,含义为 user 端的 user 对象的 login 操作
- 请求参数:json格式的code
实际开发
- 修改配置文件,添加
sky-jwt-user端的配置,添加sky-wechat的配置 - 注意:返回的VO对象中需要有User的id,所以如果是该用户不存在为新用户的时候,插入用户时,需要主键回填
- 修改配置文件,添加
1.4 review后的经验与反思
这次用户微信登录接口的开发是一个非常典型的分布式/微服务开发场景,涵盖了第三方接口对接、鉴权机制设计、数据一致性保障以及 Spring 框架的深度应用。
以下是根据你提供的代码和开发过程总结的经验与教训:
- 核心开发经验 (Best Practices)
“登录即注册”的闭环设计:
在
UserServiceImpl.login中,通过getByOpenid判断用户是否存在,若不存在则调用insert自动完成注册。这种设计简化了用户操作路径,提高了转化率。JWT 双端解耦与拦截器隔离:
针对管理端(Admin)和用户端(User)采用了不同的 JWT 密钥和 Token 名称。通过在
WebMvcConfiguration中注册两个独立的拦截器(JwtTokenAdminInterceptor和JwtTokenUserInterceptor),实现了权限的精确控制和业务逻辑的清晰解耦。ThreadLocal 的巧妙应用:
利用
BaseContext(基于ThreadLocal)存储当前登录用户的 ID。这利用了 Web 服务器“每个请求对应一个线程”的特性,使得 Service 层无需在方法参数中层层传递userId,就能轻松获取用户信息。MyBatis 主键回填机制:
在
UserMapper.xml中使用useGeneratedKeys="true"和keyProperty="id"。这保证了在执行插入操作后,Java 对象能立即获得数据库生成的自增 ID,从而让 Controller 能够将正确的userId载入 JWT 载荷中。
- 关键教训与踩坑点 (Lessons Learned)
拦截器配置的“实例混淆”风险:
教训:在配置类中注册拦截器时,如果直接复制粘贴代码,极易出现“挂羊头卖狗肉”的情况(例如给
/user/**路径注册了adminInterceptor)。对策:注册拦截器时要严格核对注入的 Bean 实例,确保拦截逻辑与请求路径完全匹配。
第三方接口响应的“空指针”隐患:
教训:直接对
jsonObject.get("openid")调用toString()是危险的。如果微信服务器因为 code 过期或 AppSecret 错误返回报错信息,JSON 中将不包含openid字段,会导致系统抛出NullPointerException。对策:使用
getString("openid")并在操作前进行非空校验,同时记录完整的响应日志以便排查问题。ThreadLocal 变量名的歧义性:
教训:在用户端拦截器中使用
empId这种变量名会产生误导。对策:虽然
BaseContext存的是Long类型,但在不同的拦截器中应使用符合当前业务语义的变量名(如userId),提高代码的可读性。事务的“防御性”意识:
教训:虽然当前登录逻辑只有一查一增,看似不需要事务。
对策:保留
@Transactional是为了应对未来可能增加的复杂业务(如注册送积分、初始化设置),并保障在高并发下的数据一致性。
- 技术栈应用总结
| 技术点 | 作用 | 体现位置 |
|---|---|---|
| HttpClient | 跨服务通讯,对接微信 API | UserServiceImpl.getOpenid |
| Fastjson | 解析第三方返回的 JSON 数据 | UserServiceImpl 解析响应体 |
| MyBatis | 数据库持久化及主键回填 | UserMapper.xml 插入逻辑 |
| Spring MVC | 拦截器链式校验与路径过滤 | WebMvcConfiguration 注册中心 |
| JWT | 无状态身份验证,双端密钥分离 | UserController 与拦截器校验逻辑 |
总结: 这次开发不仅是完成一个登录功能,更是对**“请求-拦截-校验-业务处理-持久化”**这一标准 Java Web 开发链路的一次深度实践。掌握了如何处理第三方数据关联(openid)以及如何在内存空间(ThreadLocal)安全地共享用户信息。