diff --git a/addons/syslogin/Plugin.php b/addons/syslogin/Plugin.php index 0f1acbb1..29f8f23c 100644 --- a/addons/syslogin/Plugin.php +++ b/addons/syslogin/Plugin.php @@ -13,26 +13,26 @@ namespace addons\syslogin; * @author thinkphp */ -class Plugin extends \sent\Addons{ +class Plugin extends \sent\Addons { public $info = array( - 'name'=>'Syslogin', - 'title'=>'第三方登录', - 'description'=>'第三方登录', - 'status'=>1, - 'author'=>'molong', - 'version'=>'0.1' + 'name' => 'Syslogin', + 'title' => '第三方登录', + 'description' => '第三方登录', + 'status' => 1, + 'author' => 'molong', + 'version' => '0.1', ); - public function loginBottomAddon(){ + public function loginBottomAddon() { return $this->fetch('login'); } - public function install(){ + public function install() { return true; } - public function uninstall(){ + public function uninstall() { return true; } } \ No newline at end of file diff --git a/addons/syslogin/config.php b/addons/syslogin/config.php new file mode 100644 index 00000000..78301134 --- /dev/null +++ b/addons/syslogin/config.php @@ -0,0 +1,6 @@ + ['title' => 'QQ', 'name' => 'qq', 'type' => 'fieldlist', 'value' => ['app_id' => '', 'app_secret' => '', 'callback' => '/', 'code' => '', 'scope' => '']], + 'wechat' => ['title' => 'Wechat', 'name' => 'wechat', 'type' => 'fieldlist', 'value' => ['app_id' => '', 'app_secret' => '', 'callback' => '/', 'scope' => '']], + 'weibo' => ['title' => 'Weibo', 'name' => 'weibo', 'type' => 'fieldlist', 'value' => ['app_id' => '', 'app_secret' => '', 'callback' => '/']], +]; \ No newline at end of file diff --git a/addons/syslogin/controller/Index.php b/addons/syslogin/controller/Index.php index 12aa7c3f..f9f2ac61 100644 --- a/addons/syslogin/controller/Index.php +++ b/addons/syslogin/controller/Index.php @@ -9,12 +9,60 @@ namespace addons\syslogin\controller; -class Index extends \app\controller\front\Base{ - - public function login(){ - } +use addons\syslogin\model\SyncLogin; +use think\facade\Session; - public function callback(){ - - } +class Index extends \app\controller\front\Base { + + public function login() { + $config = $this->getAddonsConfig(); + foreach ($config as $key => $value) { + $config[$key] = json_decode($value, true); + } + $app = new \addons\syslogin\service\Application($config); + $platform = $this->request->param('platform'); + return $this->redirect($app->$platform->getAuthorizeUrl()); + } + + public function callback() { + $code = $this->request->param('code'); + if (!$code) { + return $this->error("非法操作!"); + } + $config = $this->getAddonsConfig(); + foreach ($config as $key => $value) { + $config[$key] = json_decode($value, true); + } + $app = new \addons\syslogin\service\Application($config); + $platform = $this->request->param('platform', 'wechat'); + $userInfo = $app->$platform->getUserInfo(); + + Session::set("{$platform}-userinfo", $userInfo); + $sync = SyncLogin::where(['platform' => $platform, 'openid' => $userInfo['openid']])->find(); + if ($sync) { + if ($sync['uid']) { + //已绑定用户直接登录 + SyncLogin::login($userInfo); + } else { + //未绑定用户跳转绑定用户 + return $this->redirect('/addons/syslogin/index/bind/platform/' . $platform); + } + } else { + SyncLogin::register($userInfo); + //未绑定用户跳转绑定用户 + return $this->redirect('/addons/syslogin/index/bind/platform/' . $platform); + } + } + + public function bind() { + $platform = $this->request->param('platform', 'wechat'); + + $userinfo = Session::get("{$platform}-userinfo"); + + $this->data = [ + 'userinfo' => $userinfo['userinfo'], + 'platform' => $platform, + ]; + return $this->fetch(); + } } diff --git a/addons/syslogin/model/SyncLogin.php b/addons/syslogin/model/SyncLogin.php new file mode 100644 index 00000000..f0453686 --- /dev/null +++ b/addons/syslogin/model/SyncLogin.php @@ -0,0 +1,16 @@ + 'Qq', + 'weibo' => 'Weibo', + 'wechat' => 'Wechat', + ]; + + /** + * 服务对象信息 + * @var array + */ + protected $services = []; + + public function __construct($options = []) + { + $options = array_intersect_key($options, $this->providers); + $options = array_merge($this->config, is_array($options) ? $options : []); + foreach ($options as $key => &$option) { + $option['app_id'] = isset($option['app_id']) ? $option['app_id'] : ''; + $option['app_secret'] = isset($option['app_secret']) ? $option['app_secret'] : ''; + // 如果未定义回调地址则自动生成 + $option['callback'] = isset($option['callback']) && $option['callback'] ? $option['callback'] : addon_url('syslogin/index/callback', [':platform' => $key], false, true); + } + $this->config = $options; + //注册服务器提供者 + $this->registerProviders(); + } + + /** + * 注册服务提供者 + */ + private function registerProviders() + { + foreach ($this->providers as $k => $v) { + $this->services[$k] = function () use ($k, $v) { + $options = $this->config[$k]; + $objname = __NAMESPACE__ . "\\{$v}"; + return new $objname($options); + }; + } + } + + public function __set($key, $value) + { + $this->services[$key] = $value; + } + + public function __get($key) + { + return isset($this->services[$key]) ? $this->services[$key]($this) : null; + } +} diff --git a/addons/syslogin/service/Qq.php b/addons/syslogin/service/Qq.php new file mode 100644 index 00000000..30d77e7f --- /dev/null +++ b/addons/syslogin/service/Qq.php @@ -0,0 +1,132 @@ +config = array_merge($this->config, $config); + } + $this->config = array_merge($this->config, is_array($options) ? $options : []); + } + + /** + * 登陆 + */ + public function login() { + header("Location:" . $this->getAuthorizeUrl()); + } + + /** + * 获取authorize_url + */ + public function getAuthorizeUrl() { + $state = md5(uniqid(rand(), true)); + Session::set('state', $state); + $queryarr = array( + "response_type" => "code", + "client_id" => $this->config['app_id'], + "redirect_uri" => $this->config['callback'], + "scope" => $this->config['scope'], + "state" => $state, + ); + request()->isMobile() && $queryarr['display'] = 'mobile'; + $url = self::GET_AUTH_CODE_URL . '?' . http_build_query($queryarr); + return $url; + } + + /** + * 获取用户信息 + * @param array $params + * @return array + */ + public function getUserInfo($params = []) { + $params = $params ? $params : $_GET; + if (isset($params['access_token']) || (isset($params['state']) && $params['state'] == Session::get('state') && isset($params['code']))) { + //获取access_token + $data = isset($params['code']) ? $this->getAccessToken($params['code']) : $params; + $access_token = isset($data['access_token']) ? $data['access_token'] : ''; + $refresh_token = isset($data['refresh_token']) ? $data['refresh_token'] : ''; + $expires_in = isset($data['expires_in']) ? $data['expires_in'] : 0; + if ($access_token) { + $openid = $this->getOpenId($access_token); + //获取用户信息 + $queryarr = [ + "access_token" => $access_token, + "oauth_consumer_key" => $this->config['app_id'], + "openid" => $openid, + ]; + $ret = Http::get(self::GET_USERINFO_URL, $queryarr); + $userinfo = (array) json_decode($ret, true); + if (!$userinfo || !isset($userinfo['ret']) || $userinfo['ret'] !== 0) { + return []; + } + $userinfo = $userinfo ? $userinfo : []; + $userinfo['avatar'] = isset($userinfo['figureurl_qq_2']) ? $userinfo['figureurl_qq_2'] : ''; + $data = [ + 'access_token' => $access_token, + 'refresh_token' => $refresh_token, + 'expires_in' => $expires_in, + 'openid' => $openid, + 'userinfo' => $userinfo, + ]; + return $data; + } + } + return []; + } + + /** + * 获取access_token + * @param string $code + * @return array + */ + public function getAccessToken($code = '') { + if (!$code) { + return []; + } + $queryarr = array( + "grant_type" => "authorization_code", + "client_id" => $this->config['app_id'], + "client_secret" => $this->config['app_secret'], + "redirect_uri" => $this->config['callback'], + "code" => $code, + ); + $ret = Http::get(self::GET_ACCESS_TOKEN_URL, $queryarr); + $params = []; + parse_str($ret, $params); + return $params ? $params : []; + } + + /** + * 获取open_id + * @param string $access_token + * @return string + */ + private function getOpenId($access_token = '') { + $response = Http::get(self::GET_OPENID_URL, ['access_token' => $access_token]); + if (strpos($response, "callback") !== false) { + $lpos = strpos($response, "("); + $rpos = strrpos($response, ")"); + $response = substr($response, $lpos + 1, $rpos - $lpos - 1); + } + $user = (array) json_decode($response, true); + return isset($user['openid']) ? $user['openid'] : ''; + } +} diff --git a/addons/syslogin/service/Service.php b/addons/syslogin/service/Service.php new file mode 100644 index 00000000..616e7683 --- /dev/null +++ b/addons/syslogin/service/Service.php @@ -0,0 +1,87 @@ + $platform, + 'openid' => $params['openid'], + 'openname' => isset($params['userinfo']['nickname']) ? $params['userinfo']['nickname'] : '', + 'access_token' => $params['access_token'], + 'refresh_token' => $params['refresh_token'], + 'expires_in' => $params['expires_in'], + 'logintime' => $time, + 'expiretime' => $time + $params['expires_in'], + ]; + $auth = \app\common\library\Auth::instance(); + + $auth->keeptime($keeptime); + $third = Third::get(['platform' => $platform, 'openid' => $params['openid']]); + if ($third) { + $user = User::get($third['user_id']); + if (!$user) { + return false; + } + $third->save($values); + return $auth->direct($user->id); + } else { + // 先随机一个用户名,随后再变更为u+数字id + $username = Random::alnum(20); + $password = Random::alnum(6); + $domain = request()->host(); + + Db::startTrans(); + try { + // 默认注册一个会员 + $result = $auth->register($username, $password, $username . '@' . $domain, '', $extend, $keeptime); + if (!$result) { + return false; + } + $user = $auth->getUser(); + $fields = ['username' => 'u' . $user->id, 'email' => 'u' . $user->id . '@' . $domain]; + if (isset($params['userinfo']['nickname'])) { + $fields['nickname'] = $params['userinfo']['nickname']; + } + if (isset($params['userinfo']['avatar'])) { + $fields['avatar'] = htmlspecialchars(strip_tags($params['userinfo']['avatar'])); + } + + // 更新会员资料 + $user = User::get($user->id); + $user->save($fields); + + // 保存第三方信息 + $values['user_id'] = $user->id; + Third::create($values); + Db::commit(); + } catch (PDOException $e) { + Db::rollback(); + $auth->logout(); + return false; + } + + // 写入登录Cookies和Token + return $auth->direct($user->id); + } + } +} diff --git a/addons/syslogin/service/Wechat.php b/addons/syslogin/service/Wechat.php new file mode 100644 index 00000000..ad97b48e --- /dev/null +++ b/addons/syslogin/service/Wechat.php @@ -0,0 +1,122 @@ +config = array_merge($this->config, $config); + } + $this->config = array_merge($this->config, is_array($options) ? $options : []); + } + + /** + * 登陆 + */ + public function login() { + header("Location:" . $this->getAuthorizeUrl()); + } + + /** + * 获取authorize_url + */ + public function getAuthorizeUrl() { + $state = md5(uniqid(rand(), true)); + Session::set('state', $state); + $queryarr = array( + "appid" => $this->config['app_id'], + "redirect_uri" => $this->config['callback'], + "response_type" => "code", + "scope" => $this->config['scope'], + "state" => $state, + ); + request()->isMobile() && $queryarr['display'] = 'mobile'; + $url = self::GET_AUTH_CODE_URL . '?' . http_build_query($queryarr) . '#wechat_redirect'; + return $url; + } + + /** + * 获取用户信息 + * @param array $params + * @return array + */ + public function getUserInfo($params = []) { + $params = $params ? $params : request()->get(); + if (isset($params['access_token']) || (isset($params['state']) && $params['state'] == Session::get('state') && isset($params['code']))) { + //获取access_token + $data = isset($params['code']) ? $this->getAccessToken($params['code']) : $params; + $access_token = isset($data['access_token']) ? $data['access_token'] : ''; + $refresh_token = isset($data['refresh_token']) ? $data['refresh_token'] : ''; + $expires_in = isset($data['expires_in']) ? $data['expires_in'] : 0; + if ($access_token) { + $openid = isset($data['openid']) ? $data['openid'] : ''; + $unionid = isset($data['unionid']) ? $data['unionid'] : ''; + if (stripos($this->config['scope'], 'snsapi_userinfo') !== false) { + //获取用户信息 + $queryarr = [ + "access_token" => $access_token, + "openid" => $openid, + "lang" => 'zh_CN', + ]; + $client = new \GuzzleHttp\Client(); + $ret = $client->post(self::GET_USERINFO_URL, ['form_params' => $queryarr])->getBody()->getContents(); + $userinfo = (array) json_decode($ret, true); + if (!$userinfo || isset($userinfo['errcode'])) { + return []; + } + $userinfo = $userinfo ? $userinfo : []; + $userinfo['avatar'] = isset($userinfo['headimgurl']) ? $userinfo['headimgurl'] : ''; + } else { + $userinfo = []; + } + $data = [ + 'access_token' => $access_token, + 'refresh_token' => $refresh_token, + 'expires_in' => $expires_in, + 'openid' => $openid, + 'unionid' => $unionid, + 'userinfo' => $userinfo, + ]; + return $data; + } + } + return []; + } + + /** + * 获取access_token + * @param string code + * @return array + */ + public function getAccessToken($code = '') { + if (!$code) { + return []; + } + $queryarr = array( + "appid" => $this->config['app_id'], + "secret" => $this->config['app_secret'], + "code" => $code, + "grant_type" => "authorization_code", + ); + $client = new \GuzzleHttp\Client(); + $response = $client->post(self::GET_ACCESS_TOKEN_URL, ['form_params' => $queryarr])->getBody()->getContents(); + $ret = (array) json_decode($response, true); + return $ret ? $ret : []; + } +} diff --git a/addons/syslogin/service/Weibo.php b/addons/syslogin/service/Weibo.php new file mode 100644 index 00000000..b38a8312 --- /dev/null +++ b/addons/syslogin/service/Weibo.php @@ -0,0 +1,114 @@ +config = array_merge($this->config, $config); + } + $this->config = array_merge($this->config, is_array($options) ? $options : []); + } + + /** + * 登陆 + */ + public function login() { + header("Location:" . $this->getAuthorizeUrl()); + } + + /** + * 获取authorize_url + */ + public function getAuthorizeUrl() { + $state = md5(uniqid(rand(), true)); + Session::set('state', $state); + $queryarr = array( + "response_type" => "code", + "client_id" => $this->config['app_id'], + "redirect_uri" => $this->config['callback'], + "state" => $state, + ); + request()->isMobile() && $queryarr['display'] = 'mobile'; + $url = self::GET_AUTH_CODE_URL . '?' . http_build_query($queryarr); + return $url; + } + + /** + * 获取用户信息 + * @param array $params + * @return array + */ + public function getUserInfo($params = []) { + $params = $params ? $params : $_GET; + if (isset($params['access_token']) || (isset($params['state']) && $params['state'] == Session::get('state') && isset($params['code']))) { + //获取access_token + $data = isset($params['code']) ? $this->getAccessToken($params['code']) : $params; + $access_token = isset($data['access_token']) ? $data['access_token'] : ''; + $refresh_token = isset($data['refresh_token']) ? $data['refresh_token'] : ''; + $expires_in = isset($data['expires_in']) ? $data['expires_in'] : 0; + if ($access_token) { + $uid = isset($data['uid']) ? $data['uid'] : ''; + //获取用户信息 + $queryarr = [ + "access_token" => $access_token, + "uid" => $uid, + ]; + $ret = Http::get(self::GET_USERINFO_URL, $queryarr); + $userinfo = (array) json_decode($ret, true); + if (!$userinfo || isset($userinfo['error_code'])) { + return []; + } + $userinfo = $userinfo ? $userinfo : []; + $userinfo['nickname'] = isset($userinfo['screen_name']) ? $userinfo['screen_name'] : ''; + $userinfo['avatar'] = isset($userinfo['profile_image_url']) ? $userinfo['profile_image_url'] : ''; + $data = [ + 'access_token' => $access_token, + 'refresh_token' => $refresh_token, + 'expires_in' => $expires_in, + 'openid' => $uid, + 'userinfo' => $userinfo, + ]; + return $data; + } + } + return []; + } + + /** + * 获取access_token + * @param string code + * @return array + */ + public function getAccessToken($code = '') { + if (!$code) { + return ''; + } + $queryarr = array( + "grant_type" => "authorization_code", + "client_id" => $this->config['app_id'], + "client_secret" => $this->config['app_secret'], + "redirect_uri" => $this->config['callback'], + "code" => $code, + ); + $response = Http::post(self::GET_ACCESS_TOKEN_URL, $queryarr); + $ret = (array) json_decode($response, true); + return $ret ? $ret : []; + } +} diff --git a/addons/syslogin/view/index/bind.html b/addons/syslogin/view/index/bind.html new file mode 100644 index 00000000..9598a9eb --- /dev/null +++ b/addons/syslogin/view/index/bind.html @@ -0,0 +1,79 @@ +{extend name="../../../view/addon/front" /} +{block name="body"} +