微信小程序登录

你的名字 2022-11-26 07:00 327阅读 0赞

微信各接口定义(残缺版):

  1. package com.cong.security.core.properties;
  2. public class WXConstant {
  3. /** 获取access_token. */
  4. public static String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
  5. /** 根据openId获取用户信息 */
  6. public static String URL_GET_USER_INFO = "https://api.weixin.qq.com/sns/userinfo?openid=";
  7. /** 用oauth2获取用户信息. */
  8. public static String OAUTH2_USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=%s";
  9. /** 验证oauth2的access token是否有效. */
  10. public static String OAUTH2_VALIDATE_TOKEN_URL = "https://api.weixin.qq.com/sns/auth?access_token=%s&openid=%s";
  11. /** 微信小程序code换取openId接口 */
  12. public static String CODE_CHANGE_OPENID = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";
  13. /** 微信小程序消息回复发送接口 */
  14. public static String SEND_MSG = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=";
  15. }

微信小程序登录

返回值封装(code换取openId)
  1. package com.cong.security.core.social.weixin.mini.connect;
  2. import java.io.Serializable;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. @Data
  6. @NoArgsConstructor
  7. public class WxminiRes implements Serializable{
  8. private static final long serialVersionUID = 1L;
  9. private String openId;
  10. private String sessionKey;
  11. public WxminiRes(String openId, String sessionKey) {
  12. super();
  13. this.openId = openId;
  14. this.sessionKey = sessionKey;
  15. }
  16. }
微信小程序获取openID
  1. package com.cong.security.core.social.weixin.mini.connect;
  2. import java.security.Security;
  3. import org.apache.commons.lang3.StringUtils;
  4. import org.bouncycastle.jce.provider.BouncyCastleProvider;
  5. import com.cong.security.core.code.CodeException;
  6. import com.cong.security.core.properties.WXConstant;
  7. import org.springframework.web.client.RestTemplate;
  8. import com.alibaba.fastjson.JSONObject;
  9. import lombok.extern.slf4j.Slf4j;
  10. @Slf4j
  11. public class WxMiniOauth2Template {
  12. static {
  13. // BouncyCastle是一个开源的加解密解决方案,主页在http://www.bouncycastle.org/
  14. Security.addProvider(new BouncyCastleProvider());
  15. }
  16. private String appId;
  17. private String appSecret;
  18. public WxMiniOauth2Template(String appId, String appSecret) {
  19. super();
  20. this.appId = appId;
  21. this.appSecret = appSecret;
  22. }
  23. /** * 微信授权码获取openId * * @param code * 授权码 * @return 微信openId * @author single-聪 * @date 2020年7月30日 * @version 1.7.2 */
  24. public WxminiRes getOpenId(String code) {
  25. RestTemplate restTemplate = new RestTemplate();
  26. String url = String.format(WXConstant.CODE_CHANGE_OPENID, this.appId, this.appSecret, code);
  27. String res = restTemplate.getForObject(url, String.class);
  28. log.info("微信小程序登录返回值为:[{}]", res);
  29. JSONObject data = JSONObject.parseObject(res);
  30. String openId = data.getString("openid");
  31. // 返回错误码时直接返回空
  32. if ((data.getString("errcode") == null || !"0".equals(data.getString("errcode")))
  33. && StringUtils.isNotBlank(openId)) {
  34. // 额外的session_key字段
  35. return new WxminiRes(openId, data.getString("session_key"));
  36. } else {
  37. throw new CodeException("登录失败");
  38. }
  39. }
  40. }

根据前端传输的code换取微信小程序openId,CodeException为自定义异常,目的是为了异常捕获,自己定义一个即可。拿到用户openId即可实现登录。

用户信息解密

主要用于用户不存在时设置默认信息、此时需要用到获取openId是返回的session_key,所以上一步需要合理保存。

返回值封装
  1. package com.cong.security.core.social.weixin.mini.connect;
  2. import java.io.Serializable;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. @Data
  6. @NoArgsConstructor
  7. public class WeiXinMiniUserInfo implements Serializable{
  8. private static final long serialVersionUID = 1L;
  9. /** 用户唯一标识 */
  10. private String openId;
  11. /** 用户昵称 */
  12. private String nickName;
  13. /** 用户性别 */
  14. private String gender;
  15. /** 普通用户个人资料填写的城市 */
  16. private String city;
  17. /** 普通用户个人资料填写的省份 */
  18. private String province;
  19. /** 国家,如中国为CN */
  20. private String country;
  21. /** 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空 */
  22. private String avatarUrl;
  23. /** 用户统一标识。针对一个微信开放平台帐号下的应用,同一用户的unionid是唯一的。 */
  24. private String unionId;
  25. }
解密工具类
  1. package com.cong.util;
  2. import java.security.AlgorithmParameters;
  3. import java.security.Security;
  4. import java.util.Arrays;
  5. import javax.crypto.Cipher;
  6. import javax.crypto.spec.IvParameterSpec;
  7. import javax.crypto.spec.SecretKeySpec;
  8. import org.apache.commons.codec.binary.Base64;
  9. import org.bouncycastle.jce.provider.BouncyCastleProvider;
  10. import com.cong.security.core.social.weixin.mini.connect.WeiXinMiniUserInfo;
  11. import com.alibaba.fastjson.JSONObject;
  12. import lombok.extern.slf4j.Slf4j;
  13. @Slf4j
  14. public class AesUtil {
  15. static {
  16. // BouncyCastle是一个开源的加解密解决方案,主页在http://www.bouncycastle.org/
  17. Security.addProvider(new BouncyCastleProvider());
  18. }
  19. public static WeiXinMiniUserInfo decrypt(String sessionKey, String encryptedData, String iv) {
  20. // 被加密的数据
  21. byte[] dataByte = Base64.decodeBase64(encryptedData);
  22. // 加密秘钥
  23. byte[] keyByte = Base64.decodeBase64(sessionKey);
  24. // 偏移量
  25. byte[] ivByte = Base64.decodeBase64(iv);
  26. try {
  27. // 如果密钥不足16位,那么就补足. 这个if 中的内容很重要
  28. int base = 16;
  29. if (keyByte.length % base != 0) {
  30. int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
  31. byte[] temp = new byte[groups * base];
  32. Arrays.fill(temp, (byte) 0);
  33. System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
  34. keyByte = temp;
  35. }
  36. // 初始化
  37. Security.addProvider(new BouncyCastleProvider());
  38. Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
  39. SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
  40. AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
  41. parameters.init(new IvParameterSpec(ivByte));
  42. cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
  43. byte[] resultByte = cipher.doFinal(dataByte);
  44. if (null != resultByte && resultByte.length > 0) {
  45. String result = new String(resultByte, "UTF-8");
  46. log.info("解析出用户数据为:[{}]", result);
  47. WeiXinMiniUserInfo userInfo = JSONObject.parseObject(result, WeiXinMiniUserInfo.class);
  48. return userInfo;
  49. }
  50. } catch (Exception e) {
  51. log.info("微信小程序数据解析异常:[{}]", e.getMessage());
  52. }
  53. return null;
  54. }
  55. }
解密所需参数

获取用户信息


























参数 解释 获取
encryptedData 包括敏感数据在内的完整用户信息的加密数据 wx.getUserInfo(Object object)
iv 加密算法的初始向量 wx.getUserInfo(Object object)
session_key 会话密钥 code换取openId时返回

注意:可以在上一个接口中返回session_key,但是小程序官网不建议将session_key返回给前端、所以后端需要存储并且在两次接口请求中能够辨别是同一个用户,从而获取到正确的session_key,获取到正确的session_key有有效期、好像只可以使用一次而且只有最新的有效!!!所以前端的login和getUserInfo接口调用有顺序而且有次数规则。(本文为Java后端开发,前端调用简单介绍)

因为是第三方登录,所有有两种情况需要考虑:

  • 用户已注册:此时小程序用户信息解密失败也是没什么问题,毕竟不会使用这个数据覆盖自己系统中的用户信息
  • 用户未注册:此时需要考虑用户信息解密失败的情况,为了良好的用户体验,即使解密失败(可能由于网络等原因解密失败、前一步code2session验证通过实际上这个用户基本上就是真实用户了)也应该设置默认值而不是提示用户注册失败

本文使用的登录是基于Oauth2协议使用JWT生成令牌,不同项目代码嵌入方式不同,所以只封装了和微信那边的对接,至于在自己代码中的业务逻辑自行编写即可。

对接有几天了,可能部分代码会缺失,如有缺失请留言。

发表评论

表情:
评论列表 (有 0 条评论,327人围观)

还没有评论,来说两句吧...

相关阅读

    相关 程序登录

    这段时间,一直闲,断断续续写了差不多10多篇博客了。今天下午研究了下微信小程序登录,以前没有做过这方面的,趁着有资源,玩一下小程序。 第一步: 去看微信开发文档接口,这个比什

    相关 程序授权登录

    现在微信小程序非常火爆,很多常规的APP都推出了自己的微信小程序。说到微信小程序,不可避免会触及到微信账号的授权登录,现在来说说一下哈! 首先需要自己的一个小程序,可以到微

    相关 程序授权登录

    现在微信小程序非常火爆,很多常规的APP都推出了自己的微信小程序。说到微信小程序,不可避免会触及到微信账号的授权登录,现在来说说一下哈! 首先需要自己的一个小程序,可以到微