美文网首页
微信开发unionId获取和企业微信接口调用配置签名问题总结

微信开发unionId获取和企业微信接口调用配置签名问题总结

作者: 逆水清寒 | 来源:发表于2020-04-09 08:59 被阅读0次

    前言

    • 2019年我们公司的新的业务为了打通微信生态开始接入公众号,小程序的开发,到今年四月又开始打通企业微信生态。微信当时有的文档特别的坑,坑到爆炸(可能现在已经改正,暂无查证)。中间的开发工作也遇到很多的问题,耽误了很多的时间。所以把问题总结一下,防止后面犯类似的错误。

    一:微信的UnionID的获取问题

    1:问题
    • 当我们开发一个新的小程序并且需要和以前的公众号的用户打通的时候,我们发现了我们小程序的用户拿不到unionid,无法和以前的公众号做关联
    • 还有个问题就是我们的公众号测试环境的用户也没法和生产的数据进行打通。当时的微信文档只看到一个unionid的机制,没有具体的页面操作流程。
    2:原因
    • UnionId 是微信给一个用户做唯一识别的标志,是用户微信唯一的id。如果企业只有一个公众号的时候,我们是无法获取到UnionID的,因为至少要有两个及以上的应用(公众号,小程序,企业微信,app应用,网站三方应用)在微信开放平台上进行关联才可以获取到unionID
    3:解决方案
    • 在微信开放平台绑定主体(公众号,小程序),如果绑定的应用在同一个主体下面,我们就可以获取到用户的unionId
    • 微信开放平台绑定小程序和公众号
    • 开发者平台绑定的小程序也可以是体验版的小程序,绑定上以后我们的测试环境的用户就能和公众号的用户(包含生产和测试环境)的用户打通
    • 企业微信绑定小程序要到企业微信平台设置。企业微信->应用管理->小程序->关联小程序
    • 企业微信的关联应用要到企业微信->应用管理->应用->创建应用设置,如果已有应用要在第三方->添加第三方应用设置
    • 微信添加第三方应用

    二:部分用户拿不到unionId的问题

    1:问题
    • 在我们的小程序授权登录的时候,有一部分用户拿不到微信的unionId,导致我们整个流程进行不下去,查了很多的资料,都是从wx.getUserInfo()里获取,但是取的解密值是没有unionId字段的,如下
    {"phoneNumber":"15251***648","watermark":{"appid":"wx4b101b***1f6b108","timestamp":1586253485},"purePhoneNumber":"15251**648","countryCode":"86"}
    
    2:原因:
    • 暂时还没有明白微信的wx.getUserInfo什么情况下会返回unionId的值
    3:解决方案:
    • 前端如果传的微信code授权获取不到unionId,那么就要从前端第一次登录授权获取到的加密数据encryptedData和iv传到后台,然后后端再次登录授权获取到session_key 进行解密,就能获取到unionId
    • 获取用户session_key和unionId代码如下
    public String getUnionId(String code,String iv,String encryptedData) {
            RestTemplate restTemplate = new RestTemplate();
            StringBuilder unionIdRequestUrl = new StringBuilder(URI_UNIONID);
            unionIdRequestUrl.append("?appid="+appid);
            unionIdRequestUrl.append("&secret="+secret);
            unionIdRequestUrl.append("&js_code="+code);
            unionIdRequestUrl.append("&grant_type=authorization_code");
            String response = restTemplate.getForObject(unionIdRequestUrl.toString(), String.class);
            Map<String, Object> result = new ObjectMapper().readValue(response, Map.class);
            String sessionKey = MapUtils.getString(result, "session_key");
            String unionId = MapUtils.getString(result, "unionId");
            // 如果没有获取到就解析前端的加密数据
            if (null != unionId) {
                String str = AES.wxDecrypt(encryptedData, sessionKey, iv);
                JSONObject obj = JSONObject.parseObject(str);
                unionId = null != obj?obj.getString("unionId"):null;
            }
            return unionId
        }
    

    三:企业微信的签名机制和配置config获取问题

    1:问题
    • 在企业微信开发中,我们一个需求是需要获取当前聊天人的信息然后入库保存。我们制定的步骤就是前端先通过客户端的API获取当前聊天人的userId,然后再通过服务端API获取用户的信息。在前端获取客户端api的时候必须要先获取权限config的配置,这个后端提供了接口给前端,包含签名,生成签名的随机串等信息,如图
    • 但是前端通过这个获取签名获取了userId以后,再调用后端接口的时候总会报非法的签名信息。
    2:原因
    • 企业微信的调用应用接口的权限分为两步:一是获取权限验证的配置 二是获取应用的权限的配置
    • 我们通过我们的后端只返回给了前端一次签名让其去获取权限验证的config,然后前端又去调企业微信的接口,这个是行不通的,必须再获取一次应用的权限配置config,也就是要从后端获取两次签名,用两次不同的签名获取不同的config
    • 企业微信的config必须通过签名去获取,签名由后端生成返回给前端
    3:解决方案(java后端)
    • 1:返回给前端两个不同的签名
        //企业微信的jsapi_ticket地址
        private final static String GET_WX_TICKET = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=ACCESS_TOKEN";
    
        //应用tictet地址
        private final static String GET_APP_TICKET = "https://qyapi.weixin.qq.com/cgi-bin/ticket/get?access_token=ACCESS_TOKEN&type=agent_config";public Object getShareSign(@RequestBody ReqWeiXinShare req) Exception {
            // 微信唯一标识
            String appid = req.getAppid();
            // 微信调用接口凭证
            String appSecret = req.getAppSecret();
            // 分享url
            String locationUrl = req.getLocationUrl();
            if (!StringUtils.isEmpty(appid) && !StringUtils.isEmpty(appSecret)
                    && !StringUtils.isEmpty(locationUrl)) {
                String accessToken = wxCpUserService.accessToken();
                Map<String,Object> resultMap = new HashMap<>();
                Map<String, String> jsApiMap = this.sign(GET_WX_TICKET, accessToken,req.getLocationUrl());
                if (null != jsApiMap) {
                    resultMap.put("config_nonceStr",jsApiMap.get("nonceStr"));
                    resultMap.put("config_ticket",jsApiMap.get("ticket"));
                    resultMap.put("config_timestamp",jsApiMap.get("timestamp"));
                    resultMap.put("config_signature",jsApiMap.get("signature"));
                }
                Map<String, String> ticketMap = this.sign(GET_APP_TICKET, accessToken,req.getLocationUrl());
                if (null != ticketMap) {
                    resultMap.put("agent_nonceStr",ticketMap.get("nonceStr"));
                    resultMap.put("agent_ticket",ticketMap.get("ticket"));
                    resultMap.put("agent_timestamp",ticketMap.get("timestamp"));
                    resultMap.put("agent_signature",ticketMap.get("signature"));
                }
                resultMap.put("appid", appid);
                resultMap.put("url", locationUrl);
                return resultMap;
            } else {
                return null;
            }
        } 
    
    • 2:获取签名的公共方法
    private Map<String, String> sign(String url, String accessToken,String redirectUrl) {
            String ticket = this.getTicket(url,accessToken);
            Map<String, String> ret = new HashMap<String, String>();
            String noncestr = getRandomString(16);
            String timestamp = Long.toString(System.currentTimeMillis()).substring(0,10);
            String signature = "";
            String params = "jsapi_ticket=" + ticket + "&noncestr=" + noncestr
                    + "&timestamp=" + timestamp + "&url=" + redirectUrl;
            log.info("签名返回内容:{}", params);
            try {
                MessageDigest crypt = MessageDigest.getInstance("SHA-1");
                crypt.reset();
                crypt.update(params.getBytes("UTF-8"));
                signature = this.byteToHex(crypt.digest());
            } catch (NoSuchAlgorithmException var10) {
                var10.printStackTrace();
            } catch (UnsupportedEncodingException var11) {
                var11.printStackTrace();
            }
            ret.put("url", url);
            ret.put("ticket", ticket);
            ret.put("nonceStr", noncestr);
            ret.put("timestamp", timestamp);
            ret.put("signature", signature);
            return ret;
        }
    
    • 3:获取企业微信的ticket
    private String getTicket(String url,String accessToke) {
            String ticket = "";
            RestTemplate restTemplate = new RestTemplate();
            url = url.replace("ACCESS_TOKEN",accessToke);
            try {
                String response = restTemplate.getForObject(url, String.class);
                JSONObject demoJson = JSON.parseObject(response);
                if (demoJson.getString("errcode").equals("40001")) {
                    return "error";
                }
                ticket = demoJson.getString("ticket");
                System.out.println(ticket);
            } catch (Exception var6) {
                var6.printStackTrace();
            }
            return ticket;
        }
    
    • 4:随机数生成规则
     private static String getRandomString(int length){
            String keyString = "ergrfewfwdgggcvv;uihefujsncjdvngrjegeuirgverggvbergbvuigverug";
            int len = keyString.length();
            StringBuffer str = new StringBuffer();
            for(int i=0;i<length;i++){
                str.append(keyString.charAt((int) Math.round(Math.random() * (len - 1))));
            }
            return str.toString();
        }
    
    • 5:签名规则
    private String byteToHex(byte[] hash) {
            Formatter formatter = new Formatter();
            byte[] var3 = hash;
            int var4 = hash.length;
            for (int var5 = 0; var5 < var4; ++var5) {
                byte b = var3[var5];
                formatter.format("%02x", b);
            }
            String result = formatter.toString();
            formatter.close();
            return result;
        }
    
    • 6:前端根据返回的两个config配置,去请求企业微信的接口,就能调的通了

    三:总结

    • 微信的开发主要是因为文档不清楚不连贯的导致开发者浪费了大量的时间,我们遇到的主要问题一个unionId的获取不到,不知道如何配置才能获取到unionId,那个时候微信文档也没有介绍(貌似现在有了),一个前端企业微信接口调不通问题,不清楚还要两次获取config,如何获取config的签名微信的文档也没有说明怎么获取,只说了一个必填。解决了这些问题我们的后期开发也顺利了很多,希望以后腾讯系能更多支持java生态,封装一些api的sdk给我们

    相关文章

      网友评论

          本文标题:微信开发unionId获取和企业微信接口调用配置签名问题总结

          本文链接:https://www.haomeiwen.com/subject/aekqmhtx.html