三步实现微信小程序登录

三步实现微信小程序登录

一、登录流程:

上面这个是微信官方给出的。在通过 wx.login() 获取到用户登录态之后,需要维护登录态。开发者要注意不应该直接把 session_key、openid 等字段作为用户的标识或者 session 的标识,而应该自己派发一个 session 登录状态。详见下面的流程图:

1.前端调用wx.login()获取code值(developers.weixin.qq.com/miniprogram... 2.前端通过调用wx.getUserInfo获取iv、rawData、signature、encryptedData等加密数据,传递给后端(developers.weixin.qq.com/miniprogram...; 3.服务器通过code请求api换回session_key和openid; 4.服务器通过前端给的rawData 加获取的session_key使用sha1加密,计算出signature1; 5.比对前端传的signature和自己算出来的signature1是否一致(防止数据不一致); 6.用AES算法解密encryptedData里的敏感数据; 7.拿着敏感数据后做自己的逻辑; 8.通知前端登陆成功;

二、首先调用wx.login获取 临时登录凭证 code

ini 复制代码
wx.login({
  success: res => {
    // 获取到用户的 code:res.code
    const code = res.code;
    console.log("用户的code:" + res.code);
    //传入code调用服务端接口登录
    return api.userLogin.registerOrLogin({ code });
  }
})

Taro 框架 是一个开放式跨端跨框架解决方案(关于taro相关文档可以参考:taro-docs.jd.com/docs/),支持使用 React/Vue/Nerv 等框架来开发应用。taro基本上做到了将微信小程序上的api都封装了,同时也紧随着小程序的更新。这个地方也可以用taro来调用:

javascript 复制代码
const requestLogin = async () => {
  const { code } = await Taro.login();
  console.log("requestLogin code", code);
  return api.userLogin.registerOrLogin({ code });
};

三、服务端后台根据code获取openId、session_key和unionId:

less 复制代码
@PostMapping("/user-login/action/registerOrLogin")
public Response<LoginVO> registerOrLogin(@RequestBody UserLoginDTO userLoginDTO) {
  return ResponseUtils.returnObjectSuccess(userLoginService.registerOrLoginByInfo(userLoginDTO));
}
less 复制代码
public LoginVO registerOrLoginByInfo(UserLoginDTO userLoginDTO) {
    UserAccount userAccount = new UserAccount();
    result = authorizationGrantApi.wxMiniGrant(dto.getCode());
    if (result != null) {
        if (StringUtils.hasText(result.getOpenId())) {
            userAccount.setOpenId(result.getOpenId());
        }
        if (StringUtils.hasText(result.getUnionId())) {
            userAccount.setUnionId(result.getUnionId());
        }
    }
    
    if (StringUtils.hasText(dto.getEncryptedData()) && StringUtils.hasText(dto.getIv())) {
    ObjectNode userNode = weChatMiniGrantApi.decryptUser(dto.getEncryptedData(), result.getAuthorId(), dto.getIv());
    //基本信息
    if (userNode.get("openId") != null) {
        userAccount.setOpenId(userNode.get("openId").textValue());
        userAccount.setGender(userNode.get("gender").intValue());
        userAccount.setNickName(userNode.get("nickName").textValue());
        userAccount.setAvatar(userNode.get("avatarUrl").textValue());
        if (userNode.get("unionId") != null) {
            userAccount.setUnionId(userNode.get("unionId").textValue());
        }
    } else {
        userAccount.setUserPhone(userNode.get("phoneNumber").textValue());
    }
    
    LoginVO vo = new LoginVO();
    vo.setUserAccount(userAccount);
    ..........
    return vo;
}

其中的authorizationGrantApi.wxMiniGrant(dto.getCode())实现了根据code获取openId和unionId:

csharp 复制代码
/**
 * 微信小程序授权
 *
 * @param header
 * @param authCode
 * @return
 */
public AuthorizerGrantResult wxMiniGrant(YryzRequestHeader header, String authCode) {
    return this.wxMiniGrantAndDecryUser(header, authCode, null, null);
}

public AuthorizerGrantResult wxMiniGrantAndDecryUser(String authCode, String encryptedData, String iv) {
    if (weChatMiniGrantApi == null) {
        throw new BusinessException("", "暂未开放微信小程序授权");
    }

    //根据code,调用code2Session得到openid, session_key和unionId
    JsonNode respNode = weChatMiniGrantApi.miniGrant(authCode);

    /**
     * 转换基本参数
     */
    AuthorizerGrantResult base = new AuthorizerGrantResult();
    base.setOpenId(respNode.get("openid").textValue());
    if(respNode.get("unionid") != null){
        base.setUnionId(respNode.get("unionid").textValue());
    }
    base.setChannelId(IAuthorizerGrantHook.WX_MINI);

    /**
     * 解密用户信息数据
     */
    if (StringUtils.hasText(encryptedData) && StringUtils.hasText(iv)) {
        ObjectNode userNode = weChatMiniGrantApi.decryptUser(encryptedData, respNode.get("session_key").textValue(), iv);
        //基本信息
        AuthorizerGrantResult.ChannelAuthor channelAuthor = new AuthorizerGrantResult.ChannelAuthor(userNode);
        channelAuthor.setAvatar(userNode.get("avatarUrl").textValue());
        channelAuthor.setGender(userNode.get("gender").intValue());
        channelAuthor.setNickName(userNode.get("nickName").textValue());
        channelAuthor.setPhone(userNode.get("phoneNumber").textValue());
        base.setChannelAuthor(channelAuthor);
    }
    return authorizerGrantHook.successAfter(header, IAuthorizerGrantHook.WX_MINI, base, respNode);
}

上面代码中的wxMiniGrantAndDecryUser首先根据传入的code调用调用code2Session得到openid, session_key和unionId,然后从encryptedData和iv数据解密得到用户数据信息。解密用户数据信息是调用的decryptUser函数,其实现代码如下:

四、解密encryptedData数据得到用户信息:

ini 复制代码
public ObjectNode decryptUser(String encryptedData, String sessionKey, String iv) {
    // 被加密的数据
    byte[] dataByte = Base64Utils.decodeFromString(encryptedData);
    // 加密秘钥
    byte[] keyByte = Base64Utils.decodeFromString(sessionKey);
    // 偏移量
    byte[] ivByte = Base64Utils.decodeFromString(iv);
    try {
        // 如果密钥不足16位,那么就补足. 这个if 中的内容很重要
        int base = 16;
        if (keyByte.length % base != 0) {
            int groups = keyByte.length / base + 1;
            byte[] temp = new byte[groups * base];
            Arrays.fill(temp, (byte) 0);
            System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
            keyByte = temp;
        }

        // 初始化
        Security.addProvider(new BouncyCastleProvider());
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
        SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
        AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
        parameters.init(new IvParameterSpec(ivByte));
        cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
        byte[] resultByte = cipher.doFinal(dataByte);

        return objectMapper.readValue(new String(resultByte, StandardCharsets.UTF_8), ObjectNode.class);
    } catch (Exception e) {
        logger.error("用户信息解密失败:{}", e.getMessage());
    }
}
相关推荐
tcdos1 天前
不止扫码 — 微信生态深度融合(登录 + 支付 + 消息)
后端·微信小程序
小徐_23332 天前
Wot UI 2.2.0 发布:Button 新增 subtle,VideoPreview 预览体验继续增强
前端·微信小程序·uni-app
蜗牛前端4 天前
codex 全流程开发上线的高颜值礼簿小程序
前端·微信小程序
爱勇宝8 天前
我想认真做一件小事:让孩子和家长更好地互动
微信小程序·小程序·云开发
唯火锅不可辜负8 天前
避坑指南:iOS 下 scroll-view 嵌套 fixed 布局的“翻车”现场与修复
微信小程序
didiplus8 天前
运维人的随身神器:我把25个常用工具塞进了微信小程序
微信小程序
一份执念9 天前
uni-app 小程序分包限制处理与主包体积优化实战
前端·微信小程序
一份执念9 天前
ECharts 安装与使用完全指南:从全量引入到小程序分包优化
微信小程序·echarts
skiyee10 天前
🔥UniApp 仅需 5 行代码!实现所有页面中控制应用主题变化
前端·微信小程序
Jinkey12 天前
要用户手机号真的是为了打骚扰电话吗?浅谈微信生态会员账号体系与资产合并
后端·微信·微信小程序