更新第三方登录插件扩展

This commit is contained in:
2020-04-19 21:03:28 +08:00
parent 48d8de4946
commit 1a68cddd1c
18 changed files with 793 additions and 53 deletions

View File

@@ -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;
}
}

View File

@@ -0,0 +1,6 @@
<?php
return [
'qq' => ['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' => '/']],
];

View File

@@ -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();
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace addons\syslogin\model;
/**
*
*/
class SyncLogin extends \think\model {
public static function login($user) {
}
public static function register($user) {
}
}

View File

@@ -0,0 +1,67 @@
<?php
namespace addons\syslogin\service;
class Application {
/**
* 配置信息
* @var array
*/
private $config = [];
/**
* 服务提供者
* @var array
*/
private $providers = [
'qq' => '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;
}
}

View File

@@ -0,0 +1,132 @@
<?php
namespace addons\syslogin\service;
use think\facade\Config;
use think\facade\Session;
/**
* QQ
*/
class Qq {
const GET_AUTH_CODE_URL = "https://graph.qq.com/oauth2.0/authorize";
const GET_ACCESS_TOKEN_URL = "https://graph.qq.com/oauth2.0/token";
const GET_USERINFO_URL = "https://graph.qq.com/user/get_user_info";
const GET_OPENID_URL = "https://graph.qq.com/oauth2.0/me";
/**
* 配置信息
* @var array
*/
private $config = [];
public function __construct($options = []) {
if ($config = Config::get('third.qq')) {
$this->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'] : '';
}
}

View File

@@ -0,0 +1,87 @@
<?php
namespace addons\syslogin\service;
use addons\syslogin\model\Third;
use app\model\Member as User;
use think\exception\PDOException;
use think\facade\Db;
/**
* 第三方登录服务类
*
* @author Karson
*/
class Service {
/**
* 第三方登录
* @param string $platform 平台
* @param array $params 参数
* @param array $extend 会员扩展信息
* @param int $keeptime 有效时长
* @return boolean
*/
public static function connect($platform, $params = [], $extend = [], $keeptime = 0) {
$time = time();
$values = [
'platform' => $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);
}
}
}

View File

@@ -0,0 +1,122 @@
<?php
namespace addons\syslogin\service;
use think\facade\Config;
use think\facade\Session;
/**
* 微信
*/
class Wechat {
const GET_AUTH_CODE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize";
const GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token";
const GET_USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo";
/**
* 配置信息
* @var array
*/
private $config = [];
public function __construct($options = []) {
if ($config = Config::get('third.wechat')) {
$this->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 : [];
}
}

View File

@@ -0,0 +1,114 @@
<?php
namespace addons\syslogin\service;
use think\facade\Config;
use think\facade\Session;
/**
* 微博
*/
class Weibo {
const GET_AUTH_CODE_URL = "https://api.weibo.com/oauth2/authorize";
const GET_ACCESS_TOKEN_URL = "https://api.weibo.com/oauth2/access_token";
const GET_USERINFO_URL = "https://api.weibo.com/2/users/show.json";
/**
* 配置信息
* @var array
*/
private $config = [];
public function __construct($options = []) {
if ($config = Config::get('third.weibo')) {
$this->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 : [];
}
}

View File

@@ -0,0 +1,79 @@
{extend name="../../../view/addon/front" /}
{block name="body"}
<div class="text-center">
<img src="{$userinfo.avatar}" class="img-circle" width="80" height="80" alt=""/>
<div style="margin-top:15px;">{$userinfo.nickname}</div>
</div>
<div class="user-section login-section nav-tabs-custom" style="margin-top:20px;">
<ul class="logon-tab clearfix nav nav-tabs">
<li class="active"><a href="#tab-login" data-toggle="tab">绑定已有账号</a></li>
<li><a href="#tab-register" data-toggle="tab">创建新账号</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane tab-box fade in active bind-main login-main" id="tab-login">
<form name="form" id="bind-form" class="form-vertical" method="POST" role="form">
<input type="hidden" name="platform" value="{$platform}"/>
<div class="form-group">
<label class="control-label">账号</label>
<div class="controls">
<input type="text" id="username" name="username" data-rule="required" class="form-control input-lg" placeholder="用户名">
<p class="help-block"></p>
</div>
</div>
<div class="form-group">
<label class="control-label">密码</label>
<div class="controls">
<input type="password" id="password" name="password" data-rule="required;password" class="form-control input-lg" placeholder="密码">
<p class="help-block"></p>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-block btn-lg">确认绑定</button>
</div>
</form>
</div>
<div class="tab-pane tab-box fade in register-main login-main" id="tab-register">
<form name="form" id="register-form" class="form-vertical" method="POST" role="form">
<input type="hidden" name="platform" value="{$platform}"/>
<div class="form-group">
<label class="control-label">邮箱</label>
<div class="controls">
<input type="text" id="email" name="email" data-rule="required;email" class="form-control input-lg" placeholder="">
<p class="help-block"></p>
</div>
</div>
<div class="form-group">
<label class="control-label">用户名</label>
<div class="controls">
<input type="text" id="username" name="username" data-rule="required;length(3~30, true)" class="form-control input-lg" placeholder="">
<p class="help-block"></p>
</div>
</div>
<div class="form-group">
<label class="control-label">密码</label>
<div class="controls">
<input type="password" id="password" name="password" data-rule="required;password" class="form-control input-lg" placeholder="">
<p class="help-block"></p>
</div>
</div>
<div class="form-group">
<label class="control-label">手机号码</label>
<div class="controls">
<input type="text" id="mobile" name="mobile" data-rule="required;mobile" class="form-control input-lg" placeholder="">
<p class="help-block"></p>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-block btn-lg">创建账号并绑定</button>
</div>
</form>
</div>
</div>
</div>
{/block}
{block name="script"}
<script>
console.log('dd');
</script>
{/block}

View File

@@ -1 +0,0 @@
登录

View File

@@ -3,6 +3,7 @@
.third-login li{list-style: none;}
</style>
<ul class="third-login">
<li><a href="{:addons_url('syslogin/index/login', ['type' => 'qq'])}"><i class="fa fa-qq"></i> QQ登录</a></li>
<li><a href="{:addons_url('syslogin/index/login', ['type' => 'wechat'])}"><i class="fa fa-wechat"></i> 微信登录</a></li>
<li><a href="{:addons_url('syslogin/index/login', ['platform' => 'qq'])}"><i class="fa fa-qq"></i> QQ登录</a></li>
<li><a href="{:addons_url('syslogin/index/login', ['platform' => 'wechat'])}"><i class="fa fa-wechat"></i> 微信登录</a></li>
<li><a href="{:addons_url('syslogin/index/login', ['platform' => 'weibo'])}"><i class="fa fa-weibo"></i> 微博登录</a></li>
</ul>