通过微信扫描小程序码登录PC端管理后台
因业务需求,需要PC端的管理后台用户,与我们开发的内部小程序管理系统,保持用户和权限的一致性。所以考虑通过微信扫码登录获取小程序的身份。 具体业务逻辑如下: 1、PC端登录页生成当前登录的key值,带着key值请求后端接口,获取授权登录的小程序码,并轮询后端接口查看该key值对应的小程序码是否被用户扫描和授权。 2、后端生成一个带有key值参数的小程序码,并将此key值记录。 3、通过用户微信扫描小程序码,拿到对应的key值参数,将此key值和当前小程序用户的身份特征(token)传给后台,告知用户已扫描并点击登录。 4、后台标记此key值用户已登录,并记录用户登录身份,并通过PC端的轮询接口,告知其用户已登录。 5、PC端轮询到用户登录后,带着key值跳转到小程序登录中间页,在中间页PC端后台完成key值和用户信息的转换,记入session,然后跳转到对应的登录后的页面。 PC端登录页代码: function initLoginImg() {
require(["md5"], function (md5) {
var timestamp = md5.hash(Date.parse(new Date()) + "_" + parseInt(Math.random() * 100000));
$("#miniLoginCodeImg").html('image');
var timer = setInterval(function () {
get('/api/mini.login_pc/checkLogin', {'scene': timestamp}, false, function (res) {
if (res !== "0") {
window.location.href = "/admin/login/mini?scene=" + timestamp;
}
}, function (res) {
...
clearInterval(timer);
});
}, 2500);
});
}
initLoginImg();
PC端生成小程序码: /**
* 生成扫码登录小程序码
* @return Response
*/
public function miniLogin()
{
$params = $this->request->param();
$rule = [
'scene' => 'require',
];
try {
$this->validate($params, $rule);
} catch (ValidateException $e) {
return error($e->getMessage());
}
$scene = Arr::get($params, 'scene');
$path = 'pages/landing/landing';
$cacheKeyService = CacheService::MiniLoginPC;
$key = Arr::get($cacheKeyService, 'key');
$expire = Arr::get($cacheKeyService, 'expire');
Cache::set($key . $scene, '0', $expire);
$optional = [
'page' => $path,
'width' => 30
];
$app = $this->getMiniApp();
$response = $app->app_code->getUnlimit($scene, $optional);
return response($response, 200, ['Content-Type' => 'image/png'], 'html');
}
扫码登录接口: /**
* 扫码登录
* @return \think\response\Json
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function scanCode()
{
$user = $this->request->user;
$params = $this->request->param();
$rule = [
'scene' => 'require',
];
$scene = Arr::get($params, 'scene');
$cacheService = CacheService::MiniLoginPC;
$key = Arr::get($cacheService, 'key');
$isLogin = Cache::get($key . $scene);
if ($isLogin != "0" && empty($isLogin)) {
return error("登录失败,请重新扫码");
}
try {
$this->validate($params, $rule);
} catch (ValidateException $e) {
return error($e->getMessage());
}
//1、查找系统用户是否存在
$systemUser = SystemUserModel::where('openid', '=', $user->openid)->find();
//2、用户不存在则创建
if (empty($systemUser)) {
$systemUser = SystemUserService::instance()->createByWechatUser($user, $this->request);
}
//3、将用户写入redis
$expire = Arr::get($cacheService, 'expire');
Cache::set($key . $scene, $systemUser, $expire);
return success(true);
}
校验用户是否扫码接口: /**
* 校验用户是否扫码
*/
public function checkLogin()
{
$params = $this->request->param();
$rule = [
'scene' => 'require',
];
try {
$this->validate($params, $rule);
} catch (ValidateException $e) {
return error($e->getMessage());
}
$scene = Arr::get($params, 'scene');
$cacheService = CacheService::MiniLoginPC;
$key = Arr::get($cacheService, 'key');
$isLogin = Cache::get($key . $scene);
if ($isLogin != "0" && empty($isLogin)) {
return error("登录码已过期");
}
return success($isLogin);
}
登录中间页: /**
* 小程序登录
* @return \think\response\Redirect
*/
public function mini()
{
$params = $this->request->param();
$scene = Arr::get($params, 'scene');
if (empty($scene)) {
$this->error('登录场景值为空', sysuri('admin/login'));
}
$cacheConfig = CacheService::MiniLoginPC;
$key = Arr::get($cacheConfig, 'key') . $scene;
$systemUser = Cache::get($key);
if (empty($systemUser)) {
$this->error('小程序码已过期,请重新扫码', sysuri('admin/login'));
}
if (empty($systemUser['status'])) {
$this->error('该用户已禁用,请联系管理员', sysuri('admin/login'));
}
Session::set('user', $systemUser);
Session::delete("login_input_session_error");
SystemUserModel::where('id', '=', $systemUser['id'])->update([
'login_ip' => $this->request->ip(),
'login_at' => date('Y-m-d H:i:s'),
'login_num' => Db::raw('login_num+1'),
]);
sysoplog('用户登录', '登录系统后台成功');
return redirect('/admin');
}