精华内容
下载资源
问答
  • 详解微信小程序 登录获取unionid 首先公司开发了小程序, 公众号网页和app等, 之前都是用的openid来区分用户, 但openid只能标识用户在当前小程序或公众号里唯一, 我们希望用户可以在公司各个产品(比如公众号, 小程序,...
  • 如果大家使用小程序的同时还在使用公众号的话,可能会用到unionId这种功能,由于公司业务需要,我们需要使用unionId,具体使用方法,请参考微信开放平台的说明,但是在微信小程序的文档中只给出了部分语言实现的源码...
  • 主要介绍了.Net之微信小程序获取用户UnionID的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 注:没有在微信开放平台做开发者资质认证的就不要浪费时间了,没认证无法获取unionId,认证费用300元/年,emmmm…. 微信授权登录流程 第一步:wx.login获取 用户临时登录凭证code 第二步:wx.getUserInfo获取加密过...
  • 主要介绍了小程序:授权、登录、session_key、unionId的详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 要获取qq的unionid,而第三方登录并没有返回unionid,需要通过token去请求,得到unionid。这是腾讯官方提供的获取unionid的文档。
  • 2. 获取 unionid . $res = (new WeixinApp())->decryptData( $encryptedData, $iv, &$data,$session_key ) (参数说明:$encryptedData string 加密的用户数据 小程序端才能获取到 。$iv string 与用户数据一同返回的...
  • 主要介绍了微信小程序获取session_key,openid,unionid的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 之前都是用的openid来区分用户, 但openid只能标识用户在当前小程序或公众号里唯一, 我们希望用户可以在公司各个产品(比如公众号, 小程序, app里的微信登录)之间, 可以保持用户的唯一性, 还好微信给出了unionid....
  • 详解微信UnionID作用

    2020-10-17 00:07:53
    主要介绍了微信UnionID作用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 微信小程序-获取用户session_key,openid,unionid – 后端为nodejs8.0+ 步骤: 1、通过wx.login接口获取code既jscode,传递到后端; 2、后端请求 ...地址,就能获取到openid和unionid。 小程序接口promise化和封装  ...
  • 微信登录获取unionid

    2017-12-07 11:41:23
    关于第三方微信登录关于第三方微信登录关于第三方微信登录关于第三方微信登录关于第三方微信登录
  • TP5微信小程序获取openid和unionid代码 俩种方式:1获取openid 判断数据库是否存在获取unionid 2.获取openid后不存在 走官方button按钮 的getuserinfo去获取unionid
  • 我感觉最近对unionid和openid的一些知识的需求开始变多了,所以特此将一些有意义的讨论附在此处 讨论一:小程序 怎么获取 unionid 1.加入开放平台  2.获取session_key 3.wx.getUserInfo获取到encryptedData和iv 传...
  • 公众号开发需要进行一些配置, appid 和AppSecret需要填写
  • 用于微信小程序中,对登录用户的信息获取操作,主要为了获取 openID等敏感信息,对应文章请参考: http://blog.csdn.net/u011415782/article/details/79559639
  • 小程序unionid.zip

    2019-07-25 16:34:11
    微信小程序获取unionid,我都写注释只要会HTML、JS就能看懂
  • openid与unionid

    2021-03-29 21:05:24
    我感觉最近对unionid和openid的一些知识的需求开始变多了,所以特此将一些有意义的讨论附在此处 讨论一:小程序 怎么获取 unionid 1.加入开放平台  2.获取session_key 3.wx.getUserInfo获取到encryptedData和iv 传...
  • 前期小程序开发只进行到根据微信用户登录获取的code 去微信的API去获取到该用户的openId和session_key,到了第二阶段,老大让我重写OAuthManager的代码来实现微信小程序和微信公众号平台获取用户信息的优化,即将...
  • 网上很多资料但是都太杂太乱很多是复制过来复制过去,微信官方给的文档也很坑很多地方没说...获取小程序的用户unionid要通过小程序授权才可以,获取服务号的用户unionid,要通过用户关注服务号才可以,unionid是开放平

    网上很多资料但是都太杂太乱很多是复制过来复制过去,微信官方给的文档也很坑很多地方没说清,自己整理了一下。

    首先:要获取unionid服务号和小程序都要绑定到微信开放平台,并且都要认证,认证需要一定费用。

    微信开放平台地址:https://open.weixin.qq.com/

    绑定服务号和小程序

    绑定玩完成后才可以获取用户的unionid,不绑定无法获取

    获取小程序的用户unionid要通过小程序授权才可以,获取服务号的用户unionid,要通过用户关注服务号才可以,unionid是开放平台的id,小程序和服务号公众号等等都有自己的id,小程序和服务号使用openid作为用户的唯一标识,小程序openid和服务号(公众号)openid是不同的,属于两个平台,开放平台是用来关联其他平台的开放平台的id是unionid,使用unionid才可以互联所有平台

     

    获取小程序unionid:

    需要小程序的appid和appsecret,这两个要在微信公众平台获取,地址https://mp.weixin.qq.com/

    小程序和服务号(公众号)地址一样,登录的时候注意区分

    小程序端为:

    需要wx.log({})和wx.getUserInfo({})两个函数,wx.getUserInfo需要用户授权

     wx.login({
              success: res => {
                if (res.code) {
                  wx.getSetting({
                    success: res => {
                      if (res.authSetting['scope.userInfo']) {
                        // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
                        wx.getUserInfo({
                          success: res => {
                            // 可以将 res 发送给后台解码出 unionId
                            //console.log(res)
                              // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
                              // 所以此处加入 callback 以防止这种情况
                            if (self.userInfoReadyCallback) {
                              self.userInfoReadyCallback(res)
                            }
                            var config = {
                              url: 'api/get_openid',
                              data: {
                                code: res.code,//这个就是微信服务端换取的code,要传给后台
                                encryptedData:res.encryptedData,
                                iv:res.iv
                              }
                            }
                            ajax._get(config, function(res) {
                              console.log(res);
                            }, function(msg) {
                              console.log('获取用户信息失败!' + msg)
                            })
                          }
                        })
                      } else {
                        console.log('授权失败')
                          // self.relaunchToIndex()
                      }
                    }
                  })
                } else {
                  console.log('登录失败!' + res.errMsg)
                }
                // 发送 res.code 到后台换取 openId, sessionKey, unionId
              }
            })

    代码大体思路,可能有误根据自己的情况修改一下,上面的ajax请求经过了封装不能直接使用根据需要自己改一下,只要换取了code和iv,encryptedData即可

    后台获取接口为:

    https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code='.$code.'&grant_type=authorization_code

    可以通过php的file_get_contents函数直接获取josn数据

    $result = file_get_contents($url);

    数据解密可以使用php提供的函数openssl_decrypt来解密

    参数为:openssl_decrypt($data, $method, $secret_key, $options, $iv);

    //解密加密数据

    public function decrypt($data, $method, $secret_key, $options, $iv){
        return openssl_decrypt($data, $method, $secret_key, $options, $iv);
    }

     

    必须的参数为encryptedData(微信服务器端加密的数据)iv(微信服务器端返回的向量)

    $encryptedData = $_GET['ncryptedData'];
    $iv = $_GET['iv'];
    $url = 'https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code='.$code.'&grant_type=authorization_code';
    //注意如果没绑定开放平台无法获取unionid但是可以获取小程序的openid和其他信息
    $result = file_get_contents($url);
    $json_data = json_decode($result);
    $key = base64_decode($json_data->session_key);
    # iv是微信返回的向量,同样进行base64_decode操作
    $iv = base64_decode($iv);
    $aes_data = self::decrypt($encryptedData, 'AES-128-CBC', $key, 0, $iv);//可以使用微信平台提供发的加密解密方式
    $res_data = json_decode($aes_data);
    var_dump($res_data);
    $unionid = $res_data->unionId;
    $openid = $res_data->openid;

    服务号获取用户信息:

    需要用到服务好的appid和AppSecret还有ip白名单

    需要用到的curl发送方式:

    //get:
    private function http_request($url, $data = null){
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
        if (!empty($data)) {
            curl_setopt($curl, CURLOPT_POST, TRUE);
            curl_setopt($curl, CURLOPT_POSTFIELDS,$data);
        }
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
        $output = curl_exec($curl);
        curl_close($curl);
        return $output;
    }
    
    //模拟post请求
    private function requestPost($url, $data, $ssl=true) { 
        //curl完成 
        $curl = curl_init(); 
        //设置curl选项 
        curl_setopt($curl, CURLOPT_URL, $url);//URL 
        $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : ' 
        Mozilla/5.0 (Windows NT 6.1; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0FirePHP/0.7.4'; 
        curl_setopt($curl, CURLOPT_USERAGENT, $user_agent);//user_agent,请求代理信息 
        curl_setopt($curl, CURLOPT_AUTOREFERER, true);//referer头,请求来源 
        curl_setopt($curl, CURLOPT_TIMEOUT, 30);//设置超时时间 
        //SSL相关 
        if ($ssl) { 
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);//禁用后cURL将终止从服务端进行验证 
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);//检查服务器SSL证书中是否存在一个公用名(common name)。 
        } 
        // 处理post相关选项 
        curl_setopt($curl, CURLOPT_POST, true);// 是否为POST请求 
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);// 处理请求数据 
        // 处理响应结果 
        curl_setopt($curl, CURLOPT_HEADER, false);//是否处理响应头 
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);//curl_exec()是否返回响应结果 
    
        // 发出请求 
        $response = curl_exec($curl); 
        if (false === $response) { 
        echo '<br>', curl_error($curl), '<br>';
        return false; 
        } 
        curl_close($curl); 
        return $response; 
    }
    
    //获取token:
    
    //获取的地址为https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=SECRET
    
    //获取服务号token
    private function get_token(){ 
        $token_url = 'https://api.weixin.qq.com/cgi-bin/token?                
        grant_type=client_credential&appid=APPID&secret=SECRET';
        $wx_token = json_decode(self::http_request($token_url),true);
        $token = $wx_token["access_token"];
        return $token;
    }
    
    //完善点的代码可把token保存到redis或者保存到本地文件保存时间为2小时,token是由获取限制的一天最多2000次,业务量大的时候必须要保存否则不够用。
    
    
    
    //获取服务号openid
    private function get_openid($token){
        $server_openid_url = 'https://api.weixin.qq.com/cgi-bin/user/get?        
        access_token='.$token;
        $res = json_decode(self::http_request($server_openid_url),true);
        return $res["data"]['openid'];
    }
    
    
    
    //获取具体的用户信息地址:
    
    //$member_url = 'https://api.weixin.qq.com/cgi-bin/user/info?access_token='.$token.'&openid='.$openid;
    
    
    
    //获取所有服务号openid(关注过服务号的用户)
    
    public function get_userinfo(){
        set_time_limit(0);
        $token = self::get_server_token();
        $res = self::get_server_openid($token);
        //var_dump($res);
        $add_time = date("Y-m-d H:i:s");
        foreach ($res as $key => $v) {
        $member_url = 'https://api.weixin.qq.com/cgi-bin/user/info?        
        access_token='.$token.'&openid='.$v;
        $ret = json_decode(self::http_request($member_url),true);
        var_dump($ret);
        }
    }

     

    推送消息:

    设置模板:

    设置完就可以在我的模板中看到模版ID

     

    模板很多但是很多不符合自己的需求,可以自己定制提交审核通过后才可以使用。

    推送消息:

    官方文档:https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Template_Message_Interface.html#6

    用到的接口:https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=

    public function send_msg(){ 
        $token = '';//填写获取的token
        $url = 'https://api.weixin.qq.com/cgi-bin/message/template/send?access_token='.$token;
        $data = [
            'touser'=>'用户的openid',
            'template_id'=>'你申请的模板id',
            'url' => '',
            'miniprogram' => array(//没有小程序可以不加
            'appid'=>'小程序appid',
            'pagepath'=>'/pages/index'//跳转页面不需要跳转可以不写保证路径正确
            ),
            'data' => array(
            'first' => array('value' => '签到提醒'),
            'keyword1' => array('value' => '签到成功'),
            'keyword2' => array('value' => date('Y-m-d H:i:s',time())),
            'remark' => array('value' => '这是备注'),
            )
        ];
        $result = $this->requestPost($url, urldecode(json_encode($data)));
        var_dump($result);
        $returnData = json_decode($result[1],1);
        var_dump($returnData);
    }

    打印的消息如下就说明成功了,如果出错一般就是token过期或者模板id不匹配注意检查。

    {"errcode":0,"errmsg":"ok","msgid":1594417945399607298}

     

    如果没有设置行业可以通过如下接口设置:

    通过下面的接口设置所属行业好像只能设置两个行业,一个月设置一次。
    https://api.weixin.qq.com/cgi-bin/template/api_set_industry?access_token=ACCESS_TOKEN
    使用post提交数据,数据格式为json
    数组或其他形式必须转为josn
    例如:
    $data = array(
    "industry_id1"=>"1",
    "industry_id2"=>"2",
    "industry_id3"=>"3",
    );
    $data = joson_encode($data);

     

    public function set_indus(){
        $token = '获取的token';
        $url = 'https://api.weixin.qq.com/cgi-bin/template/api_set_industry?        
        access_token='.$token;
        $data = array(
            "industry_id1"=>"1",
            "industry_id2"=>"2",
        );
        $ret = self::requestPost($url, json_encode($data));
        var_dump($ret);
    }

    设置好可以在服务号上查看也可以通过代码查看

    {"errcode"40037,"errmsg""invalid template_id"}错误是模板id不匹配,在服务号中找到我的模板使用上面提供的模版ID

    微信开发调试页面:https://mp.weixin.qq.com/debug/

     

    展开全文
  • 我用thinkphp写了个公共类Common供参考 前端代码:   //刷新用户信息 function updateUserInfo(){ wx.login({ success: function(loginRes) { if (loginRes.code) { console.log('获取code成功!...
  • 企微提取用户unionid

    2021-04-22 09:52:04
    提取企业微信所有员工所拥有客户 也就是提取unionid 特此写个一站式工具类 一次性生成 获取员工用户

    前言

    业务需求 需要根据提取企业微信所有员工所拥有客户 也就是提取unionid 特此写个一站式工具类 一次性生成

    pom

    <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.73</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.12</version>
        <scope>provided</scope>
    </dependency>
    
     
    

    工具类

     package ins.platfrom.test;
    
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import ins.platfrom.dao.WXUserVoMapper;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.web.client.RestTemplate;
    
    import java.util.*;
    
    /**
     * 企微员工所绑定用户提取
     * @author zhangle
     * @create 2021-04-21 10:41
     */
    @Slf4j
    @Service
    public class QiWeiTest {
    
        /**
         * 微信token公共变量
         */
        private  String access_token;
        /**
         * HTTP线程池 这个如果需要代理需要配置 看我博客
         * https://blog.csdn.net/HezhezhiyuLe/article/details/92395041
         */
        @Autowired
        private RestTemplate bean;
        /**
         * 最终获得用户信息保存接口  自定义 不用删除
         */
        @Autowired
        private WXUserVoMapper wxUserVoMapper;
        /**
         * corpid  corpsecret 填自己的公司
         * 获取token get
         * https://work.weixin.qq.com/api/doc/90000/90135/91039 参考文档
         */
        private final String tokenurl = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=10086&corpsecret=10010";
    
        /**
         * 获取部门 post  可省略ID 不填默认拉取当前权限下可见部门
         * https://work.weixin.qq.com/api/doc/90000/90135/90208
         */
        private   String depturl = "https://qyapi.weixin.qq.com/cgi-bin/department/list?access_token=";
        /**
         * 批量获取客户详情 post
         * https://work.weixin.qq.com/api/doc/90000/90135/92994
         */
        private  String userurl = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/batch/get_by_user?access_token=";
        /**
         * 企微当前最高权限获取部门所有成员 department_id=1 fetch_child=1 递归 =0当前部门
         * https://work.weixin.qq.com/api/doc/90000/90135/90200
         */
        private  String deptuserurl = "https://qyapi.weixin.qq.com/cgi-bin/user/simplelist?fetch_child=1&department_id=";
    
        /**
         * 1
         * 获取企微token
         * @return token
         */
        public String getToken() {
            String tokenstr = bean.getForEntity(tokenurl, String.class).getBody();
            log.info("获取token:"+tokenstr);
            JSONObject tokenjson = parseStr(tokenstr);
            access_token=tokenjson.get("access_token").toString();
            return access_token;
        }
    
        /**
         * 0
         */
        public void start(){
            //获取token
            access_token=getToken();
            //获得父节点
            Set<String> treeDept = findTreeDept();
            if(treeDept==null||treeDept.size()==0){
                log.info("查询部门为空");
                return;
            }
            //迭代父节点
            for (String deptparent : treeDept) {
                //父节点下所有员工 递归
                Set<String> deptUserByDId = findDeptUserByDId(deptparent);
                int unionidByUserId = findUnionidByUserId(deptUserByDId);
                log.info("当前"+deptparent+"部门员工客户人数:"+unionidByUserId);
            }
    
        }
        /**
         * 2
         * 获取当前企微最高节点部门ID
         * 当自己不是当前公众号最高权限即不可以查看所有部门时使用
         * @return 最高父节点
         */
        public Set<String> findTreeDept() {
    
            String deptstr = bean.getForEntity(depturl+access_token, String.class).getBody();
            deptstr=iflegal(deptstr);
            if(deptstr==null||"".equals(deptstr)){
                log.info("获取部门失败");
                return null;
            }
            JSONObject jsonObject = parseStr(deptstr);
            //获取所有部门
            String dept = jsonObject.get("department").toString();
            List<JSONObject> departments = JSON.parseArray(dept, JSONObject.class);
            return  recursiondept(departments);
        }
    
        /**
         * 判断微信接口返回是否合法
         * @param resp
         * @return
         */
        public String iflegal(String resp){
            if(resp.contains("\"errcode\":0")){
                return resp;
            }
            log.info("失敗預警:"+resp);
            if(resp.contains("\"errcode\":40014")||resp.contains(" \"errcode\":42001")){
                getToken();
                log.info("恢复:"+access_token);
            }
            return null;
        }
        /**
         * 转换企微返回数据为json对象
         * @param str
         * @return
         */
        public JSONObject parseStr(String str){
            return JSON.parseObject(str);
        }
    
        /**
         * 查询部门父ID
         * @param departments 当前企微权限下所有部门 包含父子级
         * @return 最高父节点
         */
        public Set<String> recursiondept(List<JSONObject> departments){
            Set<String> deptid = new HashSet<>();
            Set<String> treedept = new HashSet<>();
    
            for (JSONObject department : departments) {
                deptid.add(department.getString("id"));
            }
            for (JSONObject department : departments) {
                String parentid = department.getString("parentid");
                //找不到父ID即为最高节点
                if(!deptid.contains(parentid)){
                    treedept.add(department.getString("id"));
                }
            }
            return treedept;
        }
    
        /**
         * 3
         * 通过部门ID查询当前部门员工 递归获取当前部门所有员工
         * @param deptId 部门ID
         * @return
         */
        public Set<String> findDeptUserByDId(String deptId){
            //获取当前部门下的所有员工 包括子节点
            String deptuserstr = bean.getForEntity(deptuserurl+deptId+"&access_token="+access_token, String.class).getBody();
            deptuserstr=iflegal(deptuserstr);
            if(deptuserstr==null||"".equals(deptuserstr)){
                log.info("获取部门成员失败");
                return null;
            }
            JSONObject jsonObject = parseStr(deptuserstr);
            String dept = jsonObject.get("userlist").toString();
    
            List<JSONObject> departments = JSON.parseArray(dept, JSONObject.class);
            if(departments==null||departments.size()==0){
                return null;
            }
            //去重
            Set<String> userids = new HashSet<>();
            for (JSONObject department : departments) {
                userids.add(department.getString("userid"));
            }
            log.info("用户ID总数:"+userids.size());
            return userids;
        }
    
        /**
         * 4
         * 企微员工通过员工ID查找用户unionid
         * @param userids 当前部门所有员工
         * @return
         */
        public int  findUnionidByUserId(Set<String> userids){
            //用户unionid集合 所有用户 包含重复  如果父级部门平行有多个 用户可能会重复 建议把人员再次汇总 我是因为确定父级只有一个有最大权限才这么写
            int count=0;
            int size = userids.size();
            //去重总用户(一个客户加了N个员工) 这个存储值几十万没问题 SQL写对加主键一把就成功
            Set<String> unionids = new HashSet<>();
            int formum=0;
            for (String userid : userids) {
                formum++;
                if(formum%1000==0){
                    log.info("循环到:"+formum+"/"+unionids.size()+"/"+size);
                }
                //实时更新token 防止失效
                Set<String> personnelUsers = handleUserByUserId(userid,userurl+access_token);
                unionids.addAll(personnelUsers);
                count+=personnelUsers.size();
    
            }
            //用户unionid集合有值就保存 可以根据业务需求 自行决定存储 IO SQL
            ArrayList<String> unionidarr = new ArrayList<>();
    
            for (String unionid : unionids) {
                unionidarr.add(unionid);
                //依据自己数据库能力分批次插入 一次插入几十万容易IO溢出
                if(unionidarr.size()==1000){
                    Integer insertnum = wxUserVoMapper.insertQiWei(unionidarr);
                    log.info(insertnum+"");
                    unionidarr.clear();
                }
            }
            if(unionidarr.size()>0){
                Integer insertnum = wxUserVoMapper.insertQiWei(unionidarr);
                log.info(insertnum+"");
            }
            log.info(" 员工总用户数:"+unionids.size()+"交叉结果集:"+count);
    
            return unionids.size();
        }
    
        /**
         * 处理单个用户所拥有的全部客户
         * @param userid 用户ID
         * @param url 实时链接
         * @return
         */
        public Set<String> handleUserByUserId(String userid,String url) {
    
            Set<String> unionids = new HashSet<>();
            Map<String, Object> params = new HashMap<>();
            params.put("userid",userid);
            //企微最大分页量
            params.put("limit",100);
            String openid = "";
            //封顶值 一个员工10W用户
            for (int i = 0; i < 1000; i++) {
                params.put("cursor",openid);
                String resp = bean.postForEntity(url, params, String.class).getBody();
                resp=iflegal(resp);
                if (resp == null ||"".equals(resp)) {
                    log.info("不合法返回:"+userid);
                    break;
                }
                if(i==999){
                   log.info("员工用户超限:"+userid);
                }
                JSONObject jsonObject = parseStr(resp);
                //员工客户列表
                List<JSONObject> external_contact_list = JSON.parseArray(jsonObject.get("external_contact_list").toString(), JSONObject.class);
                if(external_contact_list==null||external_contact_list.size()==0){
                    break;
                }
                for (JSONObject customer : external_contact_list) {
                    String external_contact = customer.getString("external_contact");
                    if(external_contact==null||"".equals(external_contact)){
                        break;
                    }
                    //用户粗略详情
                    JSONObject customercontact = parseStr(external_contact);
                    if(customercontact==null){
                        break;
                    }
                    // 可以拿到 客户微信名 name 头像 avatar 性别 gender 标识 unionid 类型 type更多详情需要换链接
                    String unionid = customercontact.getString("unionid");
                    if(unionid!=null&&!"".equals(unionid)){
                        unionids.add(unionid);
                    }
                }
    
                    String next_cursor = jsonObject.getString("next_cursor");
                    if ("".equals(next_cursor)) {
                        break;
                    }else {
                        //迭代
                        openid=next_cursor;
                    }
    
            }
            return unionids;
        }
    
    }
    
    
    展开全文
  • unionId 一个微信开放平台下的相同主体的App、公众号、小程序的unionid是相同的,这样就可以锁定是不是同一个用户 微信针对不同的用户在不同的应用下都有唯一的一个openId, 但是要想确定用户是不是同一个用户,就...

    unionId

    一个微信开放平台下的相同主体的App、公众号、小程序的unionid是相同的,这样就可以锁定是不是同一个用户

    微信针对不同的用户在不同的应用下都有唯一的一个openId, 但是要想确定用户是不是同一个用户,就需要靠unionid来区分

    同一个微信开放平台下的相同主体的 App、公众号、小程序,如果用户已经关注公众号,或者曾经登录过App或公众号,则用户打开小程序时,开发者可以直接通过 wx.login 获取到该用户UnionID,无须用户再次授权
    (解读:用户如果没有登录过app,也没有登录过公众号,也没有关注过公众号的情况下,小程序中通过 wx.login 是获取不到 unionid的)

     

    UnionID获取途径

    • 绑定了开发者帐号的小程序,可以通过下面3种途径获取UnionID。

    • 调用接口wx.getUserInfo,从解密数据中获取UnionID。注意本接口需要用户授权,请开发者妥善处理用户拒绝授权后的情况。

    • 如果开发者帐号下存在同主体的公众号,并且该用户已经关注了该公众号。开发者可以直接通过wx.login获取到该用户UnionID,无须用户再次授权。

    • 如果开发者帐号下存在同主体的公众号或移动应用,并且该用户已经授权登录过该公众号或移动应用。开发者也可以直接通过wx.login获取到该用户UnionID,无须用户再次授权

    用到的API

    • wx.login(obj)
    • wx.getUserInfo(obj)

    注意:getUserInfo此接口有调整,使用该接口将不再出现授权弹窗,请使用<button open-type="getUserInfo"></button>

    坑:

    我们一般都是先获取到微信的 unionid,然后再通过 unionid 去登录自己的网站,就可以关联到用户在自己网站上的 user_id,但是在小程序登录中,有时候可以获取到 unionid,有时候获取不到,在获取不到 unionid 的情况下,用户无法正常登录网站。

    原因:同一个微信开放平台下的相同主体的 App、公众号、小程序,如果用户已经关注公众号,或者曾经登录过App或公众号,则用户打开小程序时,开发者可以直接通过 wx.login 获取到该用户UnionID,无须用户再次授权
    (解读:用户如果没有登录过app,也没有登录过公众号,也没有关注过公众号的情况下,小程序中通过 wx.login 是获取不到 unionid的)

    所有就有两种情况:

    1. 一般情况,用户登录过关联的其他公众号

      使用 wx.login 获取code,传到后端,code换openid,unionId

       //1.login
       wx.login({
         success: function(data) {
    
           wx.request({
             url: openIdUrl,
             data: {
               code: data.code
             },
             success: function(res) {
               self.globalData.openid = res.data.openid
             },
             fail: function(res) {
               console.log('拉取用户openid失败,将无法正常使用开放接口等服务', res)
             }
           })
    
         },
         fail: function(err) {
           console.log('wx.login 接口调用失败,将无法正常使用开放接口等服务', err)
           callback(err)
         }
       })
    

    2.用户没有用过关联的公众号等

    这时候 wx.login 就获取不到 unionId 了。需要使用 wx.getUserInfo

    解决思路:通过带登录态的 wx.getUserInfo 获取到用户的加密数据 encryptedData 和加密算法的初始向量iv,然后将 encryptdata、iv 以及 code传给后端,后端再去通过接收到的encryptedData、iv以、code 以及之前的 session_key 解密出用户的 openid、unionid 等

      wx.getUserInfo({
        withCredentials:false,
        success:(obj)=>{
         
            wx.request({
                url: openIdUrl,
                data: {
                    code: data.code,
                    encryptedData : obj.encryptedData,
                    iv : obj.iv,
                },
                success: function(res) {
                    self.globalData.openid = res.data.openid
                },
                fail: function(res) {
                    console.log('拉取用户openid失败,将无法正常使用开放接口等服务', res)
                }
            })
    
    
        }
      })
    

    实际项目中,需要将两种情况整合使用

    两种方案:

    第一种:( 前端判断是否有 unionid )wx.login 向后端上传 code 并且后端返回数据以后,前端判断返回值中是否有 unionid 或者 unionid 是否为 null,null 的情况下去调用带有用户登录态的wx.getUserInfo(),然后再将微信返回的 encryptedData 和 iv 返回给后端,后端解密出相应的信息后再返回给前端;

    第二种:( 后端判断是否有 unionid )前端调用 wx.login(), wx.getUserInfo() ,把 code,encryptedData 和 iv 返回给后端,后端在拿到前端 code 之后去请求微信的接口拿 unionid,如果返回的 unionid 为空,再用的 encryptedData、iv以及之前的 session_key 解密出 unionid,后端解密出相应的信息后再返回给前端

    (以上转自:https://www.jianshu.com/p/46efa68d9033)

     

    后端解密controller

    
    /**
         * 解密用户敏感数据
         *
         * @param encryptedData 明文,加密数据
         * @param iv            加密算法的初始向量
         * @param code          用户允许登录后,回调内容会带上 code(有效期五分钟),开发者需要将 code 发送到开发者服务器后台,使用code 换取 session_key api,将 code 换成 openid 和 session_key
         * @return
         */
        @ResponseBody
        @RequestMapping(value = "/decodeUserInfo", method = RequestMethod.POST)
        public Map decodeUserInfo(String encryptedData, String iv, String code) {
     
            Map map = new HashMap();
     
            //登录凭证不能为空
            if (code == null || code.length() == 0) {
                map.put("status", 0);
                map.put("msg", "code 不能为空");
                return map;
            }
     
            //小程序唯一标识   (在微信小程序管理后台获取)
            String wxspAppid = "xxxxxxxxxxxxxx";
            //小程序的 app secret (在微信小程序管理后台获取)
            String wxspSecret = "xxxxxxxxxxxxxx";
            //授权(必填)
            String grant_type = "authorization_code";
     
     
             1、向微信服务器 使用登录凭证 code 获取 session_key 和 openid 
            //请求参数
            String params = "appid=" + wxspAppid + "&secret=" + wxspSecret + "&js_code=" + code + "&grant_type=" + grant_type;
            //发送请求
            String sr = HttpRequest.sendGet("https://api.weixin.qq.com/sns/jscode2session", params);
            //解析相应内容(转换成json对象)
            JSONObject json = JSONObject.fromObject(sr);
            //获取会话密钥(session_key)
            String session_key = json.get("session_key").toString();
            //用户的唯一标识(openid)
            String openid = (String) json.get("openid");
     
             2、对encryptedData加密数据进行AES解密 
            try {
                String result = AesCbcUtil.decrypt(encryptedData, session_key, iv, "UTF-8");
                if (null != result && result.length() > 0) {
                    map.put("status", 1);
                    map.put("msg", "解密成功");
     
                    JSONObject userInfoJSON = JSONObject.fromObject(result);
                    Map userInfo = new HashMap();
                    userInfo.put("openId", userInfoJSON.get("openId"));
                    userInfo.put("nickName", userInfoJSON.get("nickName"));
                    userInfo.put("gender", userInfoJSON.get("gender"));
                    userInfo.put("city", userInfoJSON.get("city"));
                    userInfo.put("province", userInfoJSON.get("province"));
                    userInfo.put("country", userInfoJSON.get("country"));
                    userInfo.put("avatarUrl", userInfoJSON.get("avatarUrl"));
                    userInfo.put("unionId", userInfoJSON.get("unionId"));
                    map.put("userInfo", userInfo);
                    return map;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            map.put("status", 0);
            map.put("msg", "解密失败");
            return map;
        }

     

    
     
    import org.apache.commons.codec.binary.Base64;
    import org.bouncycastle.jce.provider.BouncyCastleProvider;
     
    import javax.crypto.BadPaddingException;
    import javax.crypto.Cipher;
    import javax.crypto.IllegalBlockSizeException;
    import javax.crypto.NoSuchPaddingException;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    import java.io.UnsupportedEncodingException;
    import java.security.*;
    import java.security.spec.InvalidParameterSpecException;
     
    /**
     * Created by yfs on 2017/2/6.
     * <p>
     * AES-128-CBC 加密方式
     * 注:
     * AES-128-CBC可以自己定义“密钥”和“偏移量“。
     * AES-128是jdk自动生成的“密钥”。
     */
    public class AesCbcUtil {
     
     
        static {
            //BouncyCastle是一个开源的加解密解决方案,主页在http://www.bouncycastle.org/
            Security.addProvider(new BouncyCastleProvider());
        }
     
        /**
         * AES解密
         *
         * @param data           //密文,被加密的数据
         * @param key            //秘钥
         * @param iv             //偏移量
         * @param encodingFormat //解密后的结果需要进行的编码
         * @return
         * @throws Exception
         */
        public static String decrypt(String data, String key, String iv, String encodingFormat) throws Exception {
    //        initialize();
     
            //被加密的数据
            byte[] dataByte = Base64.decodeBase64(data);
            //加密秘钥
            byte[] keyByte = Base64.decodeBase64(key);
            //偏移量
            byte[] ivByte = Base64.decodeBase64(iv);
     
     
            try {
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
     
                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);
                if (null != resultByte && resultByte.length > 0) {
                    String result = new String(resultByte, encodingFormat);
                    return result;
                }
                return null;
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (NoSuchPaddingException e) {
                e.printStackTrace();
            } catch (InvalidParameterSpecException e) {
                e.printStackTrace();
            } catch (InvalidKeyException e) {
                e.printStackTrace();
            } catch (InvalidAlgorithmParameterException e) {
                e.printStackTrace();
            } catch (IllegalBlockSizeException e) {
                e.printStackTrace();
            } catch (BadPaddingException e) {
                e.printStackTrace();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
     
            return null;
        }
     
    }

    以上代码转自(https://blog.csdn.net/h_a_h_ahahah/article/details/80404435

    展开全文
  • 通过观察数据,发现有一部分用户是无法获取到UnionId的 也就是接口返回的参数中不包含UnionId参数 看了微信文档的解释,只要小程序在开放平台绑定,就一定会分配UnionId 网上也有用户遇到这样的情况,没有解决 问题...
  • 前言:微信开发平台为开发者提供openId用来区分用户的唯一性,但是openId只是在独立的应用内是唯一的,如果开发者拥有多个移动应用、网站应用、和公众帐号(包括小程序),可通过 UnionID 来区分用户的唯一性,因为...

    前言:微信开发平台为开发者提供openId用来区分用户的唯一性,但是openId只是在独立的应用内是唯一的,如果开发者拥有多个移动应用、网站应用、和公众帐号(包括小程序),可通过 UnionID 来区分用户的唯一性,因为只要是同一个微信开放平台帐号下所有应用,用户的 UnionID 是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,UnionID是相同的。
    微信官方文档:
    https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/union-id.html

    获取UnionID的两种方式:

    • 如果开发者帐号下存在同主体的公众号,并且该用户已经关注了该公众号。开发者可以直接通过 wx.login + code2Session 获取到该用户 UnionID
    • 从加密数据中获取UnionID

    ps : 这里要注意的是微信开放平台如果没有绑定微信小程序,不可能获取到unionId,无论哪种方式

    1. 已经关注公众号获取UnionID

    这种方式对于开发者来说是获取unionID最简单的方式,开发者可以直接通过 wx.login + code2Session 获取到该用户 UnionID。
    以下代码只针对于Java语言来演示

    package com.jinke.applets.common;
    
    import com.alibaba.fastjson.JSONObject;
    import com.jinke.utils.HttpUtil;
    import lombok.extern.slf4j.Slf4j;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * @author zsc
     * @date 2020/7/17
     */
    @Slf4j
    public class AppletsWeChatUtil {
    
        // 登录凭证校验地址
        public final static String GetPageAccessTokenUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=CODE&grant_type=authorization_code";
        // 小程序的appId以及appSecret
        private static final String appId = "xxxxxxxxxxxxx";
        private static final String appSecret = "xxxxxxxxxxxxxxx";
    
    
        /**
         * 小程序授权获取openID和unionID
         * @param code 前端通过wx.login获取的wxCode
         * @return
         */
        public static Map<String, String> oauth2GetUnion(String code) {
            String requestUrl = GetPageAccessTokenUrl.replace("APPID", appId).replace("SECRET", appSecret).replace("CODE", code);
            Map<String, String> result = new HashMap<>();
            try {
                /** 
                 * HttpUtil工具类会在下方贴出,此处也可换成自己的写法,只要是get请求就可以
                 * 此处请求返回的json数据包为:
                 * openid	string	用户唯一标识
                 * session_key	string	会话密钥
                 * unionid	string	用户在开放平台的唯一标识符
                 * errcode	number	错误码
                 * errmsg	string	错误信息
                 */
                String response = HttpUtil.get(requestUrl);
                JSONObject jsonResult = JSONObject.parseObject(response);           
                String openid = String.valueOf(jsonResult.get("openid"));
                // 若用户没有改小程序同主体公众号,则此处unionID为空
                String unionid = String.valueOf(jsonResult.get("unionid"));
                result.put("openid", openid);
                result.put("unionid",unionid);
            } catch (Exception e) {
                log.info("授权获取unionid出现异常");
                e.printStackTrace();
            }
            return result;
        }
    }
    
    

    2. 解密数据获取UnionID

    此方式针对于没有主体公众号或者做不到让用户都关注公众号的情况下获取UnionID.大致流程如下

    1. 前端js调取公开接口wx.getUserInfo获取encryptedData和iv
    2. 根据code2Session获取session_key和openid,如果有unionid直接返回
    3. 根据session_key,encryptedData和iv进行AES解密
    package com.jinke.rusi.utils;
    
    import com.alibaba.fastjson.JSONObject;
    import com.jinke.utils.HttpUtil;
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * @author zsc
     * @date 2020/7/17
     */
    @Slf4j
    public class AppletsWeChatUtil {
    
        // 登录凭证校验地址
        public final static String GetPageAccessTokenUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=CODE&grant_type=authorization_code";
        // 小程序的appId以及appSecret
        private static final String appId = "xxxxxxxxxxxxx";
        private static final String appSecret = "xxxxxxxxxxxxxxx";
    
        /**
         * 小程序授权
         * @param code WxCode
         * @param encryptedData 加密数据
         * @param iv 偏移量iv
         * @return
         */
        public static Map<String, String> oauth2GetUnionId(String code,String encryptedData,String iv) {
            String requestUrl = GetPageAccessTokenUrl.replace("APPID", appId).replace("SECRET", appSecret).replace("CODE", code);
            Map<String, String> result = new HashMap<>();
            try {
                String response = HttpUtil.get(requestUrl);
                JSONObject jsonObject = JSONObject.parseObject(response);
                String openid = String.valueOf(jsonObject.get("openid"));
                // 获取解密所需的session_key
                String session_key = String.valueOf(jsonObject.get("session_key"));
                // 通过AES解密encryptedData 获取union_id,工具类见下方
                String encryptedResult = AESUtil.decrypt(encryptedData, session_key, iv, "UTF-8");
                 /** 
                 * 此处解密之后数据包格式为:
                 * openid	 string	用户唯一标识
                 * nickName  string 昵称
                 * gender    string 性别
                 * city      string	城市
                 * province  string 省份
                 * country   string 国家
                 * avatarUrl string	头像
                 * unionId   string	用户在开放平台的唯一标识符
                 * watermark JSON	数据水印,包括appid,timestamp字段 为了校验数据的有效性
                 */
                JSONObject parseObject = JSONObject.parseObject(encryptedResult);
                // ps:此处一定要注意解密的出来的字段名为驼峰命名的unionId,openId,并非直接授权的unionid
                String unionid = String.valueOf(parseObject.get("unionId"));
                result.put("openid", openid);
                result.put("unionid",unionid);
            } catch (Exception e) {
                log.info("授权获取unionid出现异常");
                e.printStackTrace();
            }
            return result;
        }
    
    }
    
    
    工具类及依赖

    AESUtil:

    package com.jinke.rusi.utils;
    
    import net.sf.json.JSONObject;
    import org.apache.commons.codec.binary.Base64;
    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    
    import javax.crypto.BadPaddingException;
    import javax.crypto.Cipher;
    import javax.crypto.IllegalBlockSizeException;
    import javax.crypto.NoSuchPaddingException;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    import java.io.UnsupportedEncodingException;
    import java.security.*;
    import java.security.spec.InvalidParameterSpecException;
    
    /**
     * @author zsc
     * @date 2020/07/17
     * AES-128-CBC 加密方式
     * AES-128-CBC可以自己定义“密钥”和“偏移量“。
     * AES-128是jdk自动生成的“密钥”。
     */
    public class AESUtil {
    
        static {
            Security.addProvider(new BouncyCastleProvider());
        }
    
        /**
         * AES解密
         * @param data 密文,被加密的数据
         * @param key 秘钥
         * @param iv 偏移量
         * @param encodingFormat 解密后的结果需要进行的编码
         * @return
         * @throws Exception
         */
        public static String decrypt(String data, String key, String iv, String encodingFormat) throws Exception {
            //被加密的数据
            byte[] dataByte = Base64.decodeBase64(data);
            //加密秘钥
            byte[] keyByte = Base64.decodeBase64(key);
            //偏移量
            byte[] ivByte = Base64.decodeBase64(iv);
            try {
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
                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);
                if (null != resultByte && resultByte.length > 0) {
                    String result = new String(resultByte, encodingFormat);
                    return result;
                }
                return null;
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (NoSuchPaddingException e) {
                e.printStackTrace();
            } catch (InvalidParameterSpecException e) {
                e.printStackTrace();
            } catch (InvalidKeyException e) {
                e.printStackTrace();
            } catch (InvalidAlgorithmParameterException e) {
                e.printStackTrace();
            } catch (IllegalBlockSizeException e) {
                e.printStackTrace();
            } catch (BadPaddingException e) {
                e.printStackTrace();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    
    

    HttpUtil:

    package com.jinke.utils;
    
    import com.alibaba.fastjson.JSON;
    import org.apache.http.HttpEntity;
    import org.apache.http.StatusLine;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.client.methods.HttpRequestBase;
    import org.apache.http.entity.StringEntity;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.message.BasicNameValuePair;
    import org.apache.http.util.EntityUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.util.*;
    
    public class HttpUtil {
        private static final Logger LOGGER = LoggerFactory.getLogger(HttpUtil.class);
    
        public static int DATA_JSON = 1;
    
        public static int DATA_FORM = 2;
    
        private static String JSON_CONTENT_TYPE = "application/json";
    
        private static String CHARACTER = "UTF-8";
    
        private static String CONTENT_TYPE_NAME = "Content-Type";
    
        public static String get(String url) {
            return send(new HttpGet(url));
        }
    
        public static String post(String url, Map<String, Object> param, Integer dataType) {
            return post(url, param, dataType, null);
        }
    
        public static String post(String url, Map<String, Object> param, Integer dataType, Map<String, String> headers) {
            HttpPost post = new HttpPost(url);
            if (dataType == DATA_FORM) {
                Iterator localIterator;
                Map.Entry<String, String> entries;
                if (headers != null)
                    for (localIterator = headers.entrySet().iterator(); localIterator.hasNext(); ) {
                        entries = (Map.Entry) localIterator.next();
                        post.setHeader((String) entries.getKey(), (String) entries.getValue());
                    }
    
                Object paramList = new ArrayList();
                for (Map.Entry<String, Object> entry : param.entrySet()) {
                    ((List) paramList).add(new BasicNameValuePair((String) entry.getKey(), entry.getValue().toString()));
                }
                try {
                    post.setEntity(new org.apache.http.client.entity.UrlEncodedFormEntity((List) paramList));
                } catch (UnsupportedEncodingException e) {
                    LOGGER.info(e.getMessage());
                }
            } else if (dataType.intValue() == DATA_JSON) {
                post.setHeader(CONTENT_TYPE_NAME, JSON_CONTENT_TYPE);
                post.setEntity(new StringEntity(JSON.toJSONString(param), CHARACTER));
            }
            return send(post);
        }
    
        private static String send(HttpRequestBase request) {
            CloseableHttpClient client = org.apache.http.impl.client.HttpClients.createDefault();
            String result = null;
            try {
                CloseableHttpResponse response = client.execute(request);
                HttpEntity entity = response.getEntity();
                StatusLine status = response.getStatusLine();
                if (status.getStatusCode() == 200) {
                    result = EntityUtils.toString(entity);
                    EntityUtils.consume(entity);
                } else {
                    result = String.valueOf(status);
                }
                LOGGER.info("http response ------------" + result);
                response.close();
            } catch (IOException e) {
                LOGGER.info(e.getMessage());
            }
            return result;
        }
    
    }
    
    
    AES加密依赖的包
    这里需要注意的是,AES解密的时候需要用到javax.crypto.*包的类,在jdk的 jce.jar中提供,是jdk自带的库。如果是MAVEN项目,则需要在pom.xml文件中配置指定编译路径jce.jar
    如果配置路径麻烦,可以选择去maven或者gradle自行下载。
    
    Maven依赖:
    
    <!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
    <dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bcprov-jdk15on</artifactId>
        <version>1.50</version>
    </dependency>
    
    Gradle依赖:
    
    // https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on
    compile group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.50'
    
    
    
    展开全文
  • 微信获取用户基本信息(UnionID机制)
  • 如果为了保持多个产品线的用户统一的话,还需要unionid这个大神器,前提是把APP、小程序、公众号、网站这些内容绑定到微信开放平台,访问 open.weixin.qq.com。否则是获取不到unionid。 获取openid有两种方式  方式...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,611
精华内容 3,044
关键字:

unionid