文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

基于Thinkphp5+EasyWeChat+fastadmin微信小程序授权登录&获取手机号&微信公众号网页---联合授权登录

2023-09-12 22:41

关注

战前准备

使用 composer 安装 EasyWeChat

$ composer require overtrue/wechat:~4.0 -vvv

或者在composer.json文件renquire里面添加

"overtrue/wechat": "4.2.11",

接着 composer update 就可以了,不会用composer的需要现在本地配置一下,这里要提示一下如果你的php版本没有达到7.4以上不建议装高版本的EasyWeChat,一般4.x就可以了,目前遇到的问题都可以解决。

数据库字段准备

DROP TABLE IF EXISTS `fa_user`;CREATE TABLE `fa_user` (  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',  `platform` varchar(60) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '平台',  `unionid` varchar(60) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '厂商ID',  `openid` varchar(60) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT 'openid',  `nickname` varchar(60) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '昵称',  `avatar` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '头像',  `mobile` varchar(11) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '手机号',  PRIMARY KEY (`id`) USING BTREE,  UNIQUE KEY `indexes` (`openid`,`unionid`) USING BTREE) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT COMMENT='会员表';

在这里字段我就不全部展示了,这里我要说一下索引的问题,我在 fa_user 表里面添加了一个唯一组合索引目的是为了防止生成重复数据,索引方式是 B + 树 模式,因为我小程序和公众号两个平台登录所以我绑定的微信开放平台多一个 unionid 字段,在索引里面要注意如果这里 unionid 没有要设立为NULL 值,虽然为空值也是没毛病的那是因为这里是组合索引只要组合值唯一就可以了,如果是单个唯一索引一定不能为空,唯一索引最多允许有一条记录为空到了第二条数据出现就会出错了,因为违反唯一值约束了,都是空值啊,空值也是相同的一种,为什么为 NULL 就可以呢?如果多条数据为 NULL 那不应该也算是重复数据吗,在 MYSQLInnoDB 表引擎中是允许在唯一索引的字段中出现多个 NULL 值的,因为根据 NULL 的定义表示的是未知,因此两个 NULL 比较的结果既没有相等,也没有不相等,所以结果仍然是未知。根据这个定义,多个 NULL 值的存在也是不违反唯一约束的,所以是合理的,在oracel也是如此,这里是比较基础的索引概念知识但是也容易错,所以我特别的花费一段篇幅交代一下,这里是细节问题很容易出错要当一回事去看待!

配置公众号和小程序基础信息,我个人比较喜欢把公用信息放在公共类里面,这样会方便调用,当然你也可以放在其他地方,只要基于命名空间可以去调用它就可以了

namespace app\common\library;use app\common\model\Config;use EasyWeChat\Factory;class Wechat{    protected $app;    protected $config;    public function __construct($platform)    {        $this->setConfig($platform);        switch ($platform) {            case 'wxOfficialAccount':  //微信公众号                $this->app = Factory::officialAccount($this->config);                break;            case 'wxMiniProgram':      //微信小程序                $this->app = Factory::miniProgram($this->config);                break;        }    }    // 返回实例    public function getApp()    {        return $this->app;    }    //小程序:获取openid&session_key    public function code($code)    {        return $this->app->auth->session($code);    }    public function oauth()    {        return $this->app->oauth;    }    //解密信息    public function decryptData($session, $iv, $encryptData)    {        return $this->app->encryptor->decryptData($session, $iv, $encryptData);    }    public function unify($orderBody)    {        return $this->app->order->unify($orderBody);    }    public function bridgeConfig($prepayId)    {        $jssdk = $this->app->jssdk;        $config = $jssdk->bridgeConfig($prepayId, false);        return $config;    }    public function notify()    {        return $this->app;    }    //获取accessToken    public function getAccessToken()    {        $accessToken = $this->app->access_token;        $token = $accessToken->getToken(); // token 数组 token['access_token'] 字符串        return $token;    }        private function setConfig($platform) {        $debug = config('app_debug');        $defaultConfig = [            // 指定 API 调用返回结果的类型:array(default)/collection/object/raw/自定义类名            'response_type' => 'array',            'log' => [                'default' => $debug ? 'dev' : 'prod', // 默认使用的 channel,生产环境可以改为下面的 prod                'channels' => [                    // 测试环境                    'dev' => [                        'driver' => 'single',                        'path'   => ROOT_PATH . 'public/logs/wechat_login.log',                        'level'  => 'debug',                    ],                    // 生产环境                    'prod' => [                        'driver' => 'daily',                        'path'   => ROOT_PATH . 'public/logs/wechat_login.log',                        'level'  => 'info',                    ],                ],            ],        ];        $oauthConfig = [            'oauth' => [                'scopes'   => ['snsapi_userinfo'],                'callback' => request()->domain() . '/api/User/wxOfficialAccountOauth'            ],        ];        // 获取对应平台的配置        $this->config = Config::getEasyWechatConfig($platform);        // 根据框架 debug 合并 log 配置        $this->config = array_merge($this->config, $defaultConfig);        // 根据框架 平台 合并 oauth 配置        if ($platform === 'wxOfficialAccount') {            $this->config = array_merge($this->config, $oauthConfig);        }    }}

微信公众号网页授权登录

获取公众号授权code前端代码

// #ifdef H5// 微信公众号网页登录&刷新头像昵称&绑定wxOfficialAccountOauth() {if ($platform.get() !== "wxOfficialAccount") {uni.showToast({title: "请在微信浏览器中打开",icon: "none"});throw false;}let host = $platform.host();let payloadObject = {host: host,event,token: (event !== "login" && store.getters.isLogin) ? uni.getStorageSync("token") : ""};let payload = encodeURIComponent(JSON.stringify(payloadObject));let redirect_uri = encodeURIComponent(`${API_URL}user/wxOfficialAccountOauth`);let oauthUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + appid +`&redirect_uri=${redirect_uri}&response_type=code&scope=snsapi_userinfo&state=1`;uni.setStorageSync("lastPage", window.location.href);window.location = oauthUrl;},

后端代码

      public function getWxOfficialAccountCode()   {       // TODO 前期测试用于清空值 Session::set('wechat_user', NULL);       if (!Session::has('wechat_user')) {           $wechat = new Wechat('wxOfficialAccount');           $oauth = $wechat->oauth();           return $oauth->redirect()->send();       } else {           $this->success('授权登录成功(缓存)', Session::get('wechat_user'));       }   }        public function wxOfficialAccountOauth()    {        $params = $this->request->get();        $wechat = new Wechat('wxOfficialAccount');        $oauth = $wechat->oauth();        $decryptData = $oauth->user()->getOriginal();        if (empty($decryptData['openid'])) {            $this->error('code错误,请重试!');        }                $result = [];        try {            $user = \app\common\model\User::get([                'platform' => 'wxOfficialAccount',                'openid'   => $decryptData['openid']            ]);            if ($user) {                if ($user->status !== 'normal') {                    $this->error(__('Account is locked'));                }                // 每次调用都会更新用户基本数据---例如用户修改头像和昵称,这样就会实时更新数据了                $user->nickname = base64_decode(base64_encode($decryptData['nickname']));                $user->avatar = $decryptData['headimgurl'];                $user->save();                // 直接登陆                $result = $this->auth->direct($user->id);            } else {            // 写入个人数据            $decryptData['headimgurl'] = $decryptData['avatar'];                $result = $this->auth->oauthRegister($decryptData, 'wxOfficialAccount');            }        } catch (\Exception $e) {            $this->error($e->getMessage());        }        if ($result) {            $wechat_user = $this->auth->getUserInfo();            // 把用户登录信息写入session中,这样可以减少服务器不必要的开销            Session::set('wechat_user', $wechat_user);            $this->success('授权登录成功', $wechat_user);        } else {            $this->error('授权登录失败了!');        }    }

微信公小程序授权登录

接口提示:$decryptSession 解密个人用户信息只能是后端去解密,千万不要让前端解密然后后端获取,这样做后端是省事了但是风险太大了,私密信息客户端传值数据都不可信,因为 openid 和 unionid 属于个人私密信息而且关乎全局,如果这个错了那就都错了,比如:openid 前端传值 123456 也是可以的,因为后端很难去判断这个值的正确性,因为字符串长度格式都不固定,所以一定需服务端解密用户信息才是最安全的

        public function getWxMiniProgramSessionKey()    {        $code = $this->request->param('code', '');        $wechat = new Wechat('wxMiniProgram');        $decryptSession = $wechat->code($code);        if (!isset($decryptSession['session_key'])) {            $this->error('未获取session_key,请重启应用');        }                \think\Cache::set($decryptSession['session_key'], $decryptSession, 24 * 3600); // 强制1天过期        $this->success('获取session_key', $decryptSession);    }        public function wxMiniProgramLogin()    {        $params = $this->request->post();        // 入参        extract($params);        if (empty($iv) || empty($sessionKey) || empty($encryptedData)) {            $this->error('缺少必要参数!');        }        $result = [];        try {            $wechat = new Wechat('wxMiniProgram');            $decryptSession = \think\Cache::get($sessionKey);            if (!$decryptSession || !isset($decryptSession['openid'])) {                $this->error('未获取到登录态,请重试!');            }            $decryptUserInfo = $wechat->decryptData($sessionKey, $iv, $encryptedData); // 私密信息客户端传值数据都不可信,需服务端解密用户信息                        $decryptUserInfo = array_merge($decryptUserInfo, $decryptSession);            //组装decryptData            $decryptData = array_change_key_case($decryptUserInfo, CASE_LOWER); // 将数组中的所有键名修改为小写            if (empty($decryptData['openid'])) {                $this->error('code错误,请重试!');            }                        $user = \app\common\model\User::get([                'platform' => 'wxMiniProgram',                'openid'   => $decryptData['openid']            ]);            if (!empty($user)) {                if ($user->status != 'normal') {                    $this->error(__('Account is locked'));                }                $user->nickname = base64_decode(base64_encode($decryptData['nickname'])); // 为了解析昵称当中的表情信息                $user->avatar = $decryptData['avatarurl'];                $user->save();                // 直接登陆                $result = $this->auth->direct($user->id);            } else {                // 写入个人数据                $decryptData['avatarurl'] = $decryptData['avatar'];                $result = $this->auth->oauthRegister($decryptData, 'wxMiniProgram');            }        } catch (\Exception $e) {            $this->error($e->getMessage());        }        if ($result) {            $this->success('授权登录成功', $this->auth->getUserInfo());        } else {            $this->error('授权登录失败了!');        }    }

在 app\common\library 添加如下方法,我是基于fastadmin开发的,也可以改写我的数据写入方式

        public function oauthRegister($decryptData, $platform)    {        // 分参        extract($decryptData);        $params = [            'platform'  => $platform,            'unionid'   => (isset($unionid) && $unionid) ? $unionid : NULL,            'openid'    => $openid,            'nickname'  => base64_decode(base64_encode($nickname)),            'avatar'    => $avatar,            'jointime'  => time(),            'joinip'    => request()->ip(),            'logintime' => time(),            'loginip'   => request()->ip(),            'prevtime'  => time(),            'status'    => 'normal'        ];        Db::startTrans();        try {            $user = User::create($params, true);            $this->_user = User::get($user->id);            //设置Token            $this->_token = Random::uuid();            Token::set($this->_token, $user->id, $this->keeptime);            Db::commit();        } catch (Exception $e) {            $this->setError($e->getMessage());            Db::rollback();            return false;        }        return true;    }

微信小程序获取手机号

问题思考:这里有的小伙伴会想能不能把获取手机号的解密信息和小程序登录时的解密信息共用呢?答案是不能的,因为偏移量和加密串每次请求都不一样,我已经试过了确实不可以。

        public function getUserMobile()    {        $params = $this->request->post();        // 入参        extract($params);        if (empty($iv) || empty($sessionKey) || empty($encryptedData)) {            $this->error('缺少参数!');        }        try {            $wechat = new Wechat('wxMiniProgram');            $decryptData = $wechat->decryptData($sessionKey, $iv, $encryptedData);                        if (!$decryptData || !isset($decryptData['phoneNumber'])) {                $this->error('获取手机号失败,请重试!');            }            $user = $this->auth->getUser();            $user->mobile = $decryptData['phoneNumber'];            $user->save();        } catch (\Exception $e) {            $this->error($e->getMessage());        }        if (!empty($user)) {            $this->success('获取成功');        } else {            $this->error('获取失败');        }    }

来源地址:https://blog.csdn.net/weixin_43588185/article/details/124328569

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯