Skip to content

微信登录

1.1 微信小程序开发

1.1.1 介绍

  1. 开放注册范围:个人、企业、政府、媒体、其他组织,个人身份注册的小程序是没有支付功能的
  2. 接入流程:
    • 注册
    • 小程序信息完善
    • 开发小程序
    • 提交审核和发布

1.1.2 入门

  1. 小程序目录结构

    image-20260316094206118

1.2 微信登录流程

image-20260317122948821

  1. 小程序调用wx.login()方法,获得一个code,后端拿到该code,再将小程序id和小程序secret以及刚拿到的code一起传给微信的接口服务,微信接口服务就会返回该用户的openid,session_key等。我们将参数返回给小程序,小程序存储在本地,之后的请求都会携带这些参数,我们后端校验后,没问题就放行了,就实现了wxlogin。

  2. 请求接口为

    text
    GET 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

    image-20260317124855571

1.3 开发

  1. 业务规则

    • 基于微信登录功能实现小程序的登录功能
    • 如果是新用户需要自动完成注册
  2. 思路

    • 通过发送多次请求,发现同一个用户发送的 code 返回的 session_keyopenid 都是一样的,为我们存储用户信息提供了思路,是否可以以openid为用户的标识符(,session_key为对应用户的登录秘钥?)
      • 经过查阅session_key主要用于对用户数据进行加密签名验证和解密,保证前端传递给后端的数据的真实性,以及解密某些使用微信提供的接口获取到的敏感数据,保证接下来的会话是安全的
      • session_key使用注意事项:
        • 通常将其保存在 Redis 中,并与你颁发给前端的 JWT Token 建立映射关系。
        • session_key 是会过期的。当用户频繁使用小程序时,它保持有效;如果用户长时间不用,它会失效。前端可以调用 wx.checkSession() 来检查它是否过期。如果过期,需要重新走 wx.login 流程。
        • 在开发解密手机号的功能时,如果 session_key 不匹配,解密会抛出异常。这通常是因为 wx.login 被重复调用,导致旧的 session_key 失效,而后端还没更新。
    • 还有一个问题,获取用户这些登录信息后,如何将用户的名称和头像等其他信息获取到(有一个思路,如果是新用户,登录成功后,小程序端向后端发送新增用户的请求,将用户的信息发送过去,这里参考到其他小程序的实现)
      • 解决方法,暂时不管这些字段,后期由用户去个人中心自行完善
  3. 接口设计

    • Post /user/user/login ,含义为 user 端的 user 对象的 login 操作
    • 请求参数:json格式的code
  4. 实际开发

    • 修改配置文件,添加 sky-jwt-user 端的配置,添加 sky-wechat 的配置
    • 注意:返回的VO对象中需要有User的id,所以如果是该用户不存在为新用户的时候,插入用户时,需要主键回填

1.4 review后的经验与反思

这次用户微信登录接口的开发是一个非常典型的分布式/微服务开发场景,涵盖了第三方接口对接、鉴权机制设计、数据一致性保障以及 Spring 框架的深度应用。

以下是根据你提供的代码和开发过程总结的经验与教训:

  1. 核心开发经验 (Best Practices)
  • “登录即注册”的闭环设计

    UserServiceImpl.login 中,通过 getByOpenid 判断用户是否存在,若不存在则调用 insert 自动完成注册。这种设计简化了用户操作路径,提高了转化率。

  • JWT 双端解耦与拦截器隔离

    针对管理端(Admin)和用户端(User)采用了不同的 JWT 密钥和 Token 名称。通过在 WebMvcConfiguration 中注册两个独立的拦截器(JwtTokenAdminInterceptorJwtTokenUserInterceptor),实现了权限的精确控制和业务逻辑的清晰解耦。

  • ThreadLocal 的巧妙应用

    利用 BaseContext(基于 ThreadLocal)存储当前登录用户的 ID。这利用了 Web 服务器“每个请求对应一个线程”的特性,使得 Service 层无需在方法参数中层层传递 userId,就能轻松获取用户信息。

  • MyBatis 主键回填机制

    UserMapper.xml 中使用 useGeneratedKeys="true"keyProperty="id"。这保证了在执行插入操作后,Java 对象能立即获得数据库生成的自增 ID,从而让 Controller 能够将正确的 userId 载入 JWT 载荷中。

  1. 关键教训与踩坑点 (Lessons Learned)
  • 拦截器配置的“实例混淆”风险

    教训:在配置类中注册拦截器时,如果直接复制粘贴代码,极易出现“挂羊头卖狗肉”的情况(例如给 /user/** 路径注册了 adminInterceptor)。

    对策:注册拦截器时要严格核对注入的 Bean 实例,确保拦截逻辑与请求路径完全匹配。

  • 第三方接口响应的“空指针”隐患

    教训:直接对 jsonObject.get("openid") 调用 toString() 是危险的。如果微信服务器因为 code 过期或 AppSecret 错误返回报错信息,JSON 中将不包含 openid 字段,会导致系统抛出 NullPointerException

    对策:使用 getString("openid") 并在操作前进行非空校验,同时记录完整的响应日志以便排查问题。

  • ThreadLocal 变量名的歧义性

    教训:在用户端拦截器中使用 empId 这种变量名会产生误导。

    对策:虽然 BaseContext 存的是 Long 类型,但在不同的拦截器中应使用符合当前业务语义的变量名(如 userId),提高代码的可读性。

  • 事务的“防御性”意识

    教训:虽然当前登录逻辑只有一查一增,看似不需要事务。

    对策:保留 @Transactional 是为了应对未来可能增加的复杂业务(如注册送积分、初始化设置),并保障在高并发下的数据一致性。

  1. 技术栈应用总结
技术点作用体现位置
HttpClient跨服务通讯,对接微信 APIUserServiceImpl.getOpenid
Fastjson解析第三方返回的 JSON 数据UserServiceImpl 解析响应体
MyBatis数据库持久化及主键回填UserMapper.xml 插入逻辑
Spring MVC拦截器链式校验与路径过滤WebMvcConfiguration 注册中心
JWT无状态身份验证,双端密钥分离UserController 与拦截器校验逻辑

总结: 这次开发不仅是完成一个登录功能,更是对**“请求-拦截-校验-业务处理-持久化”**这一标准 Java Web 开发链路的一次深度实践。掌握了如何处理第三方数据关联(openid)以及如何在内存空间(ThreadLocal)安全地共享用户信息。