jiujiukeji 发表于 2019-4-21 13:21

微信授权登录

微信的授权登录和QQ、新浪等平台的授权登录都大同小异,均采用OauthOAuth2.0鉴权方式。
微信授权分为两种:

[*]静默授权
[*]弹窗授权,需要用户手动同意

两种scope的区别说明

[*]以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)
[*]以snsapi_userinfo为scope发起的网页授权,是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。
用户管理类接口中的“获取用户基本信息接口”,是在用户和公众号产生消息交互或关注后事件推送后,才能根据用户OpenID来获取用户基本信息。这个接口,包括其他微信接口,都是需要该用户(即openid)关注了公众号后,才能调用成功的。

具体而言,网页授权流程分为四步:

[*]引导用户进入授权页面同意授权,获取code
[*]通过code换取网页授权access_token(与基础支持中的access_token不同)
[*]如果需要,开发者可以刷新网页授权access_token,避免过期
[*]通过网页授权access_token和openid获取用户基本信息(支持UnionID机制)
以下是封装的微信操作类,需要用到两个数据表,用于保存access_token、ticket,由于他们具有一定有效期,且每天请求数有上限,所以开发者需自行保存


<?php
/**
*   微信操作表
*   wxtoken 表结构
*   id
*   access_token
*   addtime
*   wxticket 表结构
*   id
*   ticket
*   addtime
*/
class WX {
        private $appid;
        private $appserect;
        private $curl;
        private $msg;
        protected $errs = array(
                '-1' => '系统繁忙,此时请开发者稍候再试',
                '0' => '请求成功',
                '40001' => 'AppSecret错误或者AppSecret不属于这个公众号,请开发者确认AppSecret的正确性',
                '40002' => '请确保grant_type字段值为client_credential',
                '40164' => '调用接口的IP地址不在白名单中,请在接口IP白名单中进行设置。',
        );
        function __construct($appid, $appserect) {
                $this->appid = $appid;
                $this->appserect = $appserect;
                $this->curl = new Curl();
        }
        /*
        微信网页授权登录需要在公众号设置 - 功能设置 - 网页授权域名
        第一步:用户同意授权,获取code
        scope : snsapi_base 只能获取openid 直接跳转
        snsapi_userinfo
        */
        public function getCode($redirect_uri, $scope = 'snsapi_userinfo',$state = '1') {
                $url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid={$this->appid}&redirect_uri={$redirect_uri}&response_type=code&scope={$scope}&state={$state}#wechat_redirect";
                header("Location:{$url}");
                exit;
        }
        /*
        第二步:通过code换取网页授权access_token
        */
        public function getAccessTokenByCode($code) {
                $url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={$this->appid}&secret={$this->appserect}&code={$code}&grant_type=authorization_code";
                // exit($url);
                // $curl = new Curl();
                $result = $this->curl->doGet($url);
                if (!$result) {
                        // $this->curl->getError()
                        $this->msg = "获取token失败";
                        return false;
                }
                $result = json_decode($result, true);
                if ($result['errcode']) {
                        $this->msg = $result['errmsg'];
                        return false;
                }
                return $result;
        }
        // 第三步:刷新access_token(如果需要) 通过code 获取openid $type 0静默授权 1弹窗授权
        public function getUserInfo($code, $type = 0, $lang = 'zh_CN ') {
                $result = $this->getAccessTokenByCode($code);
                        if (!$result) {
                        return false;
                }
                $member = C::t(PT_USER)->getByOpenid($result['openid']);
        if ($member) {
                return $member;
        } else {
                if ($type) {
                        $url = "https://api.weixin.qq.com/sns/userinfo?access_token={$result['access_token']}&openid={$result['openid']}&lang={$lang}";
                        // $return = $this->curl->doGet($url);
                        // 这接口有病 强制显示文件头
                        $return = file_get_contents($url);
                        if (!$return) {
                                $this->msg = '获取用户信息失败';
                                return false;
                        }
                        $return = json_decode($return, true);
                        if (!$return) {
                                $this->msg = '获取用户信息返回失败';
                                return false;
                        }
                        // file_put_contents('ccc.txt',print_r($return,true),FILE_APPEND);
                        $data = array(
                                'openid' => $return['openid'],
                                'name' => $return['nickname'],
                                'sex' => $return['sex'],
                                'province' => $return['province'],
                                'city' => $return['city'],
                                'country' => $return['country'],
                                'img' => $return['headimgurl'],
                                'bindtel' => 0,
                        );
                } else {
                        $data = array(
                                'openid' => $result['openid'],
                                'username' => "微信用户_" . random(6,1)
                        );
                }
                $name = rand(100000, 1000000000);
                $e = $name . "@qq.com";
                $password = $e;
                $id = UserAddEdit(0, $data['username'], $password, $e,10,0,"", $msg);
                if ($id <= 0) {
                        $this->msg = $msg;
                        return false;
                }
                C::t(PT_USER)->update($data, $id);
                $member = C::t(PT_USER)->get($id);
                return $member;
                }
        }
        /*
        公众号 安全中心 设置IP白名单
        公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。
        */
        public function getAccessToken($type) {
                $addtime = TIMESTAMP - 7200;
                $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->appid}&secret={$this->appserect}";
                $row = C::t(PT_WXTOKEN)->getNew($addtime, $type);
                if ($row) {
                        return $row['access_token'];
                } else {
                        $result = $this->curl->doGet($url);
                        if (!$result) {
                                $this->msg = "无法获取令牌内容";
                                return false;
                        }
                        $result = json_decode($result, true);
                        if (!$result) {
                                $this->msg = "解析令牌内容失败";
                                return false;
                        }
                        if ($result['access_token']) {
                                C::t(PT_WXTOKEN)->addToken($result['access_token'], $type);
                                return $result['access_token'];
                        } else {
                                $this->msg = "获取令牌失败";
                                return false;
                        }
                }
        }
        // 获取js票据需要在公众号设置 - 功能设置 - JS接口安全域名设置
        public function getJsTicket() {
                $addtime = TIMESTAMP - 7200;
                $row = C::t(PT_WXTICKET)->getNew($addtime);
                if ($row) {
                        return $row['ticket'];
                } else {
                        $token = $this->getAccessToken();
                        if (!$token) {
                                return false;
                        }
                        $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={$token}&type=jsapi";
                        $result = $this->curl->doGet($url);
                        if (!$result) {
                                $this->msg = "无法获取js票据";
                                return false;
                        }
                        $result = json_decode($result, true);
                        if (!$result) {
                                $this->msg = "解析js票据内容失败";
                                return false;
                        }
                        if ($result['ticket']) {
                                C::t(PT_WXTICKET)->addTicket($result['ticket']);
                                return $result['ticket'];
                        } else {
                                $this->msg = "获取js票据失败";
                                return false;
                        }
                }
        }
        // js sdk 票据签名 当前网页的URL,不包含#及其后面部分
        public function jsSign($data) {
                // 1.所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)
                ksort($data);
                // 2.URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1 采用原始值,不进行URL 转义
                $string1 = $this->ToUrlParams($data);
                // echo "string1:{$string1}<br/>";
                // 3.对string1做sha1加密
                $sign = sha1($string1);
                // echo "signature:{$sign}<br/>";
                return $sign;
        }
        // 获取消息内容
        public function getMsg() {
                return $this->msg;
        }
        /**
        * 格式化参数格式化成url参数
        */
        public function ToUrlParams($data) {
                $buff = "";
                foreach ($data as $k => $v) {
                        if ($k != "sign" && $v != "" && !is_array($v)) {
                                $buff .= $k . "=" . $v . "&";
                        }
                }
                $buff = trim($buff, "&");
                return $buff;
        }
}
?>




// 微信登录
function wxlogin() {
        global $_G,$identifier,$config,$wx;
        if (!$_G['uid']) {
                if ($_GET['state']) {
                        //回调
                        $member = $wx->getUserInfo($_GET['code']);
                        if (!$member) {
                                exit($wx->getMsg());
                        }
                        if (!function_exists("setloginstatus")) {
                                include_once libfile('function/member');
                        }
                        // 设置登录状态$wx
                        setloginstatus($member, 2592000);
                        checkfollowfeed();
                        $_G['uid'] = $member['uid'];
                        $_G['member'] = $member;
                } else {
                        //请求授权 对参数编码
                        $redirect = urlencode(getProtocol() . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
                        $wx->getCode($redirect, 'snsapi_base');
                }
        }
}
function getProtocol() {
        return is_HTTPS() ? 'https://' : 'http://';
}
function is_HTTPS() {if ($_SERVER['HTTPS'] === 1 || $_SERVER['HTTPS'] === 'on' || $_SERVER['SERVER_PORT'] == 443) {
                return true;
        }
        return false;
}

#Web.py 发表于 2019-8-26 12:45

tianyang 发表于 2019-8-7 14:52
您好,楼主,想请教下,这个怎么来拿到电脑上或手机上,直接使用呢,本人懂这些代码,有什么好方法吗?谢谢

微信开发只能通过微信开发者工具来调试,如果是开发网页的话,选择微信网页模式就可以调试了

13780914665 发表于 2020-5-22 14:44

有一些微信被封禁了,但是可以临时登陆,但是进入网页就会提示该微信被封禁,是不是可以用这个方法给这种临时登陆的微信授权登陆

czyuyu 发表于 2019-4-21 13:41

感谢分享!

会磨牙的大核桃 发表于 2019-4-21 14:05

虽然和我用的语言不一样, 但是一样支持

古画成灰 发表于 2019-4-21 14:37

淹没在各种代码的海洋中。。

l101 发表于 2019-4-21 15:00

还不会,先收藏留着参考

13729181580 发表于 2019-4-21 15:31

真的有用么?我就可以做个程序了

www.52pojie.cn 发表于 2019-4-21 15:42

我是小马,明天来我这报道

rickw 发表于 2019-4-22 12:01

这个是只能在微信内置的浏览器里面使用?

jiujiukeji 发表于 2019-4-22 12:10

rickw 发表于 2019-4-22 12:01
这个是只能在微信内置的浏览器里面使用?

对的这个不是第三方登录

1070885984 发表于 2019-5-14 17:28

这接口有病 强行显示头部
页: [1] 2 3
查看完整版本: 微信授权登录