From 68ad74a64501e2e58aec0d44c4f3c41c869f13d0 Mon Sep 17 00:00:00 2001 From: wixy Date: Thu, 27 Jan 2022 16:31:04 +0800 Subject: [PATCH] init --- QuickAuthLogin/Action.php | 216 ++++++++++++++++++++++++++++++ QuickAuthLogin/Plugin.php | 189 ++++++++++++++++++++++++++ QuickAuthLogin/views/authbind.php | 88 ++++++++++++ QuickAuthLogin/views/login.php | 79 +++++++++++ README.md | 60 ++++++++- 5 files changed, 631 insertions(+), 1 deletion(-) create mode 100644 QuickAuthLogin/Action.php create mode 100644 QuickAuthLogin/Plugin.php create mode 100644 QuickAuthLogin/views/authbind.php create mode 100644 QuickAuthLogin/views/login.php diff --git a/QuickAuthLogin/Action.php b/QuickAuthLogin/Action.php new file mode 100644 index 0000000..75ab11c --- /dev/null +++ b/QuickAuthLogin/Action.php @@ -0,0 +1,216 @@ +haslogin()) { + // 获取当前用户名 + $name = $user->__get('name'); + + $options = QuickAuthLogin_Plugin::getoptions(); + + $db = Typecho_Db::get(); + $db->query($db->update('table.users')->rows(['qa_openid' => null, 'qa_nickname' => null, 'qa_avatar' => null])->where('name = ?', $name)); + + $ret['code'] = 200; + $ret['msg'] = ''; + $this->widget('Widget_Notice')->set(_t('当前用户绑定信息重置成功', $name, $nickName[1]), 'success'); + //$res->redirect(QuickAuthLogin_Plugin::tourl('QuickAuthLogin/auth-bind')); + } else { + $ret['msg'] = 'what are you doing?'; + } + $res->throwJson($ret); + } + + /* 二维码授权绑定 */ + public function authbind() + { + $path = QuickAuthLogin_Plugin::PLUGIN_PATH . 'views/authbind.php'; + require_once $path; + } + + /* 微信Callback跳转登录逻辑 */ + public function wechatlogin() + { + $options = QuickAuthLogin_Plugin::getoptions(); + $res = new Typecho_Response(); + $req = new Typecho_Request(); + $ret = []; + + $code = $req->get('code'); + $state = $req->get('state'); + + $api = $options->qauth_api."/user?code=".$code."&appkey=".$options->qauth_app_key."&secret=".$options->qauth_user_secret; + $paras['header'] = 1; + $body=self::get_curl($api, $paras); + preg_match('/\"code\":(.*?),/', $body, $code); + $ret['code'] = $code[1]; + preg_match('/\"msg\":\"(.*?)\"/', $body, $msg); + if($ret['code'] == 0){ + preg_match('/\"openId\":\"(.*?)\"/', $body, $openId); + preg_match('/\"nickName\":\"(.*?)\"/', $body, $nickName); + preg_match('/\"avatarUrl\":\"(.*?)\"/', $body, $avatarUrl); + + if($state == "binding"){ + require_once __TYPECHO_ROOT_DIR__ . __TYPECHO_ADMIN_DIR__ . 'common.php'; + $name = $user->__get('name'); + $key = $options->key; + + $db = Typecho_Db::get(); + + $user = $db->fetchRow($db->select()->from('table.users')->where( 'qa_openid' . ' = ?', $openId[1])->limit(1)); + if($user){ + $this->widget('Widget_Notice')->set('此微信账号已被绑定!', 'error'); + $res->redirect(QuickAuthLogin_Plugin::tourl('QuickAuthLogin/auth-bind')); + } + //更新基础信息 + $db->query($db->update('table.users')->rows(['qa_openid' => $openId[1], 'qa_nickname' => $nickName[1], 'qa_avatar' => $avatarUrl[1]])->where('name = ?', $name)); + $this->widget('Widget_Notice')->set(_t('用户 %s 成功绑定微信账号 %s', $name, $nickName[1]), 'success'); + $res->redirect(QuickAuthLogin_Plugin::tourl('QuickAuthLogin/auth-bind')); + } + else{ + $ret['login']['msg'] = 'Fail'; + $ret['login']['code'] = 0; + $db = Typecho_Db::get(); + $user = $db->fetchRow($db->select()->from('table.users')->where( 'qa_openid' . ' = ?', $openId[1])->limit(1)); + + if($user){ + $authCode = function_exists('openssl_random_pseudo_bytes') ? bin2hex(openssl_random_pseudo_bytes(16)) : sha1(Typecho_Common::randString(20)); + $user['authCode'] = $authCode; + + Typecho_Cookie::set('__typecho_uid', $user['uid'], $expire); + Typecho_Cookie::set('__typecho_authCode', Typecho_Common::hash($authCode), $expire); + + $db->query($db->update('table.users')->expression('logged', + 'activated')->rows(['authCode' => $authCode])->where('uid = ?', $user['uid'])); + + /** 压入数据 */ + $this->push($user); + $this->_user = $user; + $this->_hasLogin = true; + + echo 'success'; + $res->redirect(Helper::options()->adminUrl); + } + else{//该微信账号未绑定 + if($options->allow_register){//匿名账号注册登录 + $hasher = new PasswordHash(8, true); + $generatedPassword = Typecho_Common::randString(7); + + $newUserName = "wx_".$nickName[1]; + $existUser = $db->fetchRow($db->select()->from('table.users')->where( 'name' . ' = ?', $newUserName)->limit(1)); + if($existUser) + $newUserName = "wx_".$nickName[1].'_'.Typecho_Common::randString(4); + + $dataStruct = array( + 'name' => $newUserName, + 'mail' => $newUserName."@qauth.cn", + 'screenName'=> $nickName[1], + 'password' => $hasher->HashPassword($generatedPassword), + 'created' => time(), + 'group' => 'subscriber', + 'qa_openid' => $openId[1], + 'qa_nickname' => $nickName[1], + 'qa_avatar' => $avatarUrl[1] + ); + + $dataStruct = $this->pluginHandle()->register($dataStruct); + $insertId = $db->query($db->insert('table.users')->rows($dataStruct)); + $user = $db->fetchRow($db->select()->from('table.users')->where( 'qa_openid' . ' = ?', $openId[1])->limit(1)); + + $authCode = function_exists('openssl_random_pseudo_bytes') ? bin2hex(openssl_random_pseudo_bytes(16)) : sha1(Typecho_Common::randString(20)); + $user['authCode'] = $authCode; + Typecho_Cookie::set('__typecho_uid', $user['uid'], $expire); + Typecho_Cookie::set('__typecho_authCode', Typecho_Common::hash($authCode), $expire); + + $db->query($db->update('table.users')->expression('logged', + 'activated')->rows(['authCode' => $authCode])->where('uid = ?', $user['uid'])); + + $this->push($user); + $this->_user = $user; + $this->_hasLogin = true; + + $this->widget('Widget_Notice')->set(_t('用户 %s 已经成功注册, 密码为 %s', $newUserName, $generatedPassword), 'success'); + $res->redirect(Helper::options()->adminUrl); + + + } + else{ + $this->widget('Widget_Notice')->set('该微信未绑定用户,无法登陆!', 'error'); + $res->redirect(Helper::options()->loginUrl); + } + } + } + $res->throwJson($ret); + } + else{ + $ret['msg'] = $msg[1]; + $res->throwJson($ret); + } + } + + /** Curl单例封装函数 */ + public static function get_curl($url, $paras = []) + { + //echo $paras; + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + $httpheader[] = "Accept:*/*"; + $httpheader[] = "Accept-Encoding:gzip,deflate,sdch"; + $httpheader[] = "Accept-Language:zh-CN,zh;q=0.8"; + $httpheader[] = "Connection:close"; + if($paras['httpheader']){ + curl_setopt($ch, CURLOPT_HTTPHEADER, $paras['httpheader']); + } + else{ + curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader); + } + if ($paras['ctime']) { // 连接超时 + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, $paras['ctime']); + } + if ($paras['rtime']) { // 读取超时 + curl_setopt($ch, CURLOPT_TIMEOUT_MS, $paras['rtime']); + } + if ($paras['post']) { + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $paras['post']); + } + if ($paras['header']) { + curl_setopt($ch, CURLOPT_HEADER, true); + } + if ($paras['cookie']) { + curl_setopt($ch, CURLOPT_COOKIE, $paras['cookie']); + } + if ($paras['refer']) { + curl_setopt($ch, CURLOPT_REFERER, $paras['refer']); + } + if ($paras['ua']) { + curl_setopt($ch, CURLOPT_USERAGENT, $paras['ua']); + } else { + curl_setopt($ch, CURLOPT_USERAGENT, + "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36"); + } + if ($paras['nobody']) { + curl_setopt($ch, CURLOPT_NOBODY, 1); + } + curl_setopt($ch, CURLOPT_ENCODING, "gzip"); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $ret = curl_exec($ch); + curl_close($ch); + return $ret; + } + +} diff --git a/QuickAuthLogin/Plugin.php b/QuickAuthLogin/Plugin.php new file mode 100644 index 0000000..11dfd9b --- /dev/null +++ b/QuickAuthLogin/Plugin.php @@ -0,0 +1,189 @@ + navBar = array(__class__, 'render'); + Typecho_Plugin::factory('admin/header.php')-> header = array(__class__,'login'); + Typecho_Plugin::factory('Widget_User')-> loginSucceed = array(__class__,'afterlogin'); + + Helper::addRoute('bind',__TYPECHO_ADMIN_DIR__.'QuickAuthLogin/bind','QuickAuthLogin_Action','bind'); + Helper::addRoute('login',__TYPECHO_ADMIN_DIR__.'QuickAuthLogin/login','QuickAuthLogin_Action','login'); + Helper::addRoute('wechatlogin',__TYPECHO_ADMIN_DIR__.'QuickAuthLogin/wechatlogin','QuickAuthLogin_Action','wechatlogin'); + Helper::addRoute('reset',__TYPECHO_ADMIN_DIR__.'QuickAuthLogin/reset','QuickAuthLogin_Action','reset'); + Helper::addRoute('auth-bind',__TYPECHO_ADMIN_DIR__.'QuickAuthLogin/auth-bind','QuickAuthLogin_Action','authbind'); + + } + + public static function updateDb() + { + $db = Typecho_Db::get(); + $prefix = $db->getPrefix(); + if ("Pdo_Mysql" === $db->getAdapterName() || "Mysql" === $db->getAdapterName()) { + $sql = "ALTER TABLE `{$prefix}users` ADD COLUMN `qa_openid` varchar(64); + ALTER TABLE `{$prefix}users` ADD COLUMN `qa_nickname` varchar(64); + ALTER TABLE `{$prefix}users` ADD COLUMN `qa_avatar` varchar(255); + "; + $db->query($sql); + } else { + throw new Typecho_Plugin_Exception(_t('对不起, 本插件仅支持MySQL数据库。')); + } + + return "数据表新增字段成功!"; + } + + /** + * 禁用插件方法,如果禁用失败,直接抛出异常 + * + * @static + * @access public + * @return void + * @throws Typecho_Plugin_Exception + */ + public static function deactivate(){ + $info = self::uninstallDb(); + + } + + + public static function uninstallDb() + { + $db = Typecho_Db::get(); + $prefix = $db->getPrefix(); + $sql = "ALTER TABLE `{$prefix}users` DROP COLUMN `qa_openid`; + ALTER TABLE `{$prefix}users` DROP COLUMN `qa_nickname`; + ALTER TABLE `{$prefix}users` DROP COLUMN `qa_avatar`; + "; + $db->query($sql); + + return "数据表删除字段成功!"; + } + + /** + * 获取插件配置面板 + * + * @static + * @access public + * @param Typecho_Widget_Helper_Form $form 配置面板 + * @return void + */ + public static function config(Typecho_Widget_Helper_Form $form){ + + + $user = Typecho_Widget::widget('Widget_User'); + + //var_dump($user); + $api = new Typecho_Widget_Helper_Form_Element_Text('qauth_api',null,'https://api.qauth.cn',_t('QuickAuthApi:'),_t('QuickAuthApi地址,正常情况下无需修改')); + $form->addInput($api); + + $appkey = new Typecho_Widget_Helper_Form_Element_Text('qauth_app_key',null,'',_t('AppKey:'),_t('QuickAuth后台创建应用时的AppKey 获取AppKey')); + //var_dump($appkey); + $form->addInput($appkey); + $encryptscrypt = new Typecho_Widget_Helper_Form_Element_Text('qauth_user_secret',null,'',_t('UserSecret:'),_t('QuickAuth用户的数据加密密钥 获取UserSecret')); + $form->addInput($encryptscrypt); + + $off = new Typecho_Widget_Helper_Form_Element_Radio('off',array('0'=>'开启','1'=>'关闭'),0,_t('账户密码登录:',''),'默认开启,如需关闭,请确保账号已经绑定微信,否则将无法正常登录后台;如果出现这种情况,请重装插件解决!'); + $form->addInput($off); + + $allowRegister = new Typecho_Widget_Helper_Form_Element_Radio('allow_register',array('0'=>'否','1'=>'是'),0,_t('允许未绑定微信账号扫码登录:',''),'开启后使用没有绑定账号的微信扫码后自动注册新账号登录!'); + $form->addInput($allowRegister); + + $users = new Typecho_Widget_Helper_Form_Element_Radio('users',array('0'=>'否','1'=>'是'),0,_t('非管理员启用:',''),'启用后在导航栏增加微信账号绑定入口'); + $form->addInput($users); + + $username = $user->__get('name'); + $openid = $user->__get('qa_openid'); + $nickname = $user->__get('qa_nickname'); + + echo ''; + + } + + /** + * 个人用户的配置面板 + * + * @access public + * @param Typecho_Widget_Helper_Form $form + * @return void + */ + public static function personalConfig(Typecho_Widget_Helper_Form $form){ + } + + public static function afterlogin($this_, $name, $password, $temporarily, $expire){ + + $options = self::getoptions(); + if($options->off === '1'){ + echo 'what are you doing?'; + // 登录之前没有合适的插入点,这里强制退出 + $this_ -> logout(); + } + } + + + public static function login($header){ + + /** 获取链接信息 */ + $baseurl = Typecho_Request::getInstance()->getBaseUrl(); + + /** 判断是否登录 */ + if($baseurl == __TYPECHO_ADMIN_DIR__.'login.php'){ + + /** 清空输出缓存区 */ + ob_clean(); + + require_once self::PLUGIN_PATH.'views/login.php'; + + ob_end_flush(); + exit(); + }else{ + return $header; + } + } + + public static function render(){ + $options = self::getoptions(); + if($options->users){ + echo '' . _t('微信账号绑定') . ''; + } + } + + /** 生成URL,解决部分博客未开启伪静态,仅对本插件有效 */ + public static function tourl($action){ + return Typecho_Common::url(__TYPECHO_ADMIN_DIR__.$action, Helper::options()->index); + } + + /** 获取插件配置 */ + public static function getoptions(){ + return Helper::options()->plugin(QuickAuthLogin_Plugin::PLUGIN_NAME); + } +} \ No newline at end of file diff --git a/QuickAuthLogin/views/authbind.php b/QuickAuthLogin/views/authbind.php new file mode 100644 index 0000000..1677311 --- /dev/null +++ b/QuickAuthLogin/views/authbind.php @@ -0,0 +1,88 @@ +__get('name'); +$openid = $user->__get('qa_openid'); +$nickName = $user->__get('qa_nickname'); +$avatarUrl = $user->__get('qa_avatar'); +$option = QuickAuthLogin_Plugin::getoptions(); +$group = $user->__get('group'); + + +if($group != 'administrator' && !$option->users){ //非管理员且[非管理员启用]处于否 + throw new Typecho_Widget_Exception(_t('禁止访问'), 403); +} +?> + + + + + + + + QuickAuthLogin - 扫描登录授权绑定 + + + + + + + + +
+ +
+ + + + + + + + + diff --git a/QuickAuthLogin/views/login.php b/QuickAuthLogin/views/login.php new file mode 100644 index 0000000..05d01bf --- /dev/null +++ b/QuickAuthLogin/views/login.php @@ -0,0 +1,79 @@ +hasLogin()) { + $response->redirect($options->adminUrl); +} +if($option->off == '1'){ + $response->redirect($option->qauth_api."/qrconnect?appkey=".$option->qauth_app_key.'&state=login'); +} + +$rememberName = htmlspecialchars(Typecho_Cookie::get('__typecho_remember_name')); +Typecho_Cookie::delete('__typecho_remember_name'); +$header = ' + + +'; +?> + + + + + + + + <?php _e('%s - %s - Powered by Typecho', $menu->title, $options->title);?> + + + + + +
+ +
+ + + diff --git a/README.md b/README.md index a9966b1..b07261a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,60 @@ # QuickAuthLogin -wechat login plugin for typecho + +基于[QuickAuth](https://qauth.cn)扫码登录平台开发的Typecho微信扫码登录插件 + +## 起始 + +本插件是基于 QuickAuth 开发的 插件,使用前需要进入[QuickAuth平台](https://qauth.cn)注册配置自己的应用 + +如需修改插件或开发自己的接入项目,请参考 [QuickAuth接入文档](https://qauth.cn/doc/index.html) + +插件地址:[https://github.com/mr-wixy/QuickAuthLogin](https://github.com/mr-wixy/QuickAuthLogin) + +(请勿与其它同类插件同时启用,以免互相影响) + +## 使用方法 + +第 1 步:下载本插件,解压,放到 `usr/plugins/` 目录中; + +第 2 步:文件夹名改为 `QuickAuthLogin`; + +第 3 步:登录管理后台,激活插件; + +第 4 步:登录QuickAuth网站创建接入应用; + +![](https://cdn.wixy.cn/blog-picture/blog-picture20220127160420.png) + +
+ +第 5 步:填写应用的基本信息(注意:此时可以获取到AppKey,回调地址请填写自己博客的域名+/index.php/admin/QuickAuthLogin/wechatlogin 此处必须为https) + +![](https://cdn.wixy.cn/blog-picture/blog-picture20220127160707.png) + +第 6 步:发布应用; + +![发布应用](https://cdn.wixy.cn/blog-picture/blog-picture20220127161055.png) + +第 7 步:[获取](https://qauth.cn/config/secret)UserSecretKey; + +![](https://cdn.wixy.cn/blog-picture/blog-picture20220127161157.png) + +第 8 步:进入博客插件后台配置AppKey和UserSecret; + +![](https://cdn.wixy.cn/blog-picture/20220127161859.png) + +
+ +## 重要说明 + +1. QuickAuthApi 默认配置,正常情况下无需修改(除非QuickAuth网站接口地址改了) +2. 账户密码登录默认开启,如需关闭,请确保账号已经绑定微信,否则将无法正常登录后台;如果出现这种情况,请重装插件解决! +3. 允许未绑定微信账号扫码登录开启后,未绑定的微信扫码则会自动注册账号 +4. 非管理员启用选项开启后会在导航栏增加微信账号绑定入口 + +## 与我联系 + +作者:wixy + +如果有任何意见或发现任何BUG请联系我 + +博客:[https://blog.wixy.cn/](https://blog.wixy.cn/) \ No newline at end of file