feat(auth): 新增谷歌验证码登录 implement Google Authenticator support for two-factor authentication
- Add Google Authenticator integration for enhanced login security-Update admin edit page to include login type selection - Modify login process to support two-factor authentication - Add new database fields for login type and GA secret - Update client-side JavaScript to handle GA code input and validation
This commit is contained in:
@@ -64,15 +64,20 @@ class Index extends AdminController
|
||||
$rule = [];
|
||||
$this->validate($post, $rule);
|
||||
try {
|
||||
$save = $row
|
||||
->allowField(['head_img', 'phone', 'remark', 'update_time'])
|
||||
->save($post);
|
||||
}catch (Exception $e) {
|
||||
$login_type = $post['login_type'] ?? 1;
|
||||
if ($login_type == 2) {
|
||||
$ga_secret = (new SystemAdmin())->where('id', $id)->value('ga_secret');
|
||||
if (empty($ga_secret)) $this->error('请先绑定谷歌验证器');
|
||||
}
|
||||
$save = $row->allowField(['head_img', 'phone', 'remark', 'update_time', 'login_type'])->save($post);
|
||||
}catch (\PDOException $e) {
|
||||
$this->error('保存失败');
|
||||
}
|
||||
$save ? $this->success('保存成功') : $this->error('保存失败');
|
||||
}
|
||||
$this->assign('row', $row);
|
||||
$notes = (new SystemAdmin())->notes;
|
||||
$this->assign('notes', $notes);
|
||||
return $this->fetch();
|
||||
}
|
||||
|
||||
@@ -80,9 +85,6 @@ class Index extends AdminController
|
||||
* 修改密码
|
||||
* @param Request $request
|
||||
* @return string
|
||||
* @throws DataNotFoundException
|
||||
* @throws DbException
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public function editPassword(Request $request): string
|
||||
{
|
||||
@@ -122,4 +124,37 @@ class Index extends AdminController
|
||||
return $this->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置谷歌验证码
|
||||
* @param Request $request
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function set2fa(Request $request): string
|
||||
{
|
||||
$id = $this->adminUid;
|
||||
$row = (new SystemAdmin())->withoutField('password')->find($id);
|
||||
if (!$row) $this->error('用户信息不存在');
|
||||
// You can see: https://gitee.com/wolf-code/authenticator
|
||||
$ga = new \Wolfcode\Authenticator\google\PHPGangstaGoogleAuthenticator();
|
||||
if (!$request->isAjax()) {
|
||||
$old_secret = $row->ga_secret;
|
||||
$secret = $ga->createSecret(32);
|
||||
$ga_title = $this->isDemo ? 'EasyAdmin8演示环境' : '可自定义修改显示标题';
|
||||
$dataUri = $ga->getQRCode($ga_title, $secret)->getDataUri();
|
||||
$this->assign(compact('row', 'dataUri', 'old_secret', 'secret'));
|
||||
return $this->fetch();
|
||||
}
|
||||
$this->isDemo && $this->error('演示环境下不允许修改');
|
||||
$post = $request->post();
|
||||
$ga_secret = $post['ga_secret'] ?? '';
|
||||
$ga_code = $post['ga_code'] ?? '';
|
||||
if (empty($ga_code)) $this->error('请输入验证码');
|
||||
if (!$ga->verifyCode($ga_secret, $ga_code)) $this->error('验证码错误');
|
||||
$row->ga_secret = $ga_secret;
|
||||
$row->login_type = 2;
|
||||
$row->save();
|
||||
$this->success('操作成功');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -56,6 +56,11 @@ class Login extends AdminController
|
||||
if ($admin->status == 0) {
|
||||
$this->error('账号已被禁用');
|
||||
}
|
||||
if ($admin->login_type == 2) {
|
||||
if (empty($post['ga_code'])) $this->error('请输入谷歌验证码', ['is_ga_code' => true]);
|
||||
$ga = new \Wolfcode\Authenticator\google\PHPGangstaGoogleAuthenticator();
|
||||
if (!$ga->verifyCode($admin->ga_secret, $post['ga_code'])) $this->error('谷歌验证码错误');;
|
||||
}
|
||||
$admin->login_num += 1;
|
||||
$admin->save();
|
||||
$admin = $admin->toArray();
|
||||
|
||||
@@ -10,6 +10,13 @@ class SystemAdmin extends TimeModel
|
||||
|
||||
protected $deleteTime = 'delete_time';
|
||||
|
||||
public array $notes = [
|
||||
'login_type' => [
|
||||
1 => '密码登录',
|
||||
2 => '密码 + 谷歌验证码登录'
|
||||
],
|
||||
];
|
||||
|
||||
public function getAuthList()
|
||||
{
|
||||
$list = (new SystemAuth())
|
||||
|
||||
@@ -30,6 +30,15 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">登录方式</label>
|
||||
<div class="layui-input-block">
|
||||
{foreach notes.login_type as $key=>$val}
|
||||
<input type="radio" name="login_type" lay-skin="primary" title="{$val}" value="{$key}" lay-filter="loginType-filter" {if $key==$row.login_type}checked=""{/if}>
|
||||
{/foreach}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-form-text">
|
||||
<label class="layui-form-label">备注信息</label>
|
||||
<div class="layui-input-block">
|
||||
|
||||
45
app/admin/view/index/set2fa.html
Normal file
45
app/admin/view/index/set2fa.html
Normal file
@@ -0,0 +1,45 @@
|
||||
<div class="layuimini-container">
|
||||
<form id="app-form" class="layui-form layuimini-form" autocomplete="off">
|
||||
{if $old_secret}
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">提示</div>
|
||||
<div class="layui-card-body">
|
||||
当前账号已经绑定过了 谷歌验证码 ,如果重新保存将替换
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label required">验证秘钥</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="ga_secret" class="layui-input" value="{$secret}" readonly disabled>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label required">二维码</label>
|
||||
<div class="layui-input-block">
|
||||
<img src="{$dataUri}" alt="二维码" style="width: 200px;height: 200px">
|
||||
<div class="layui-text layui-font-cyan layui-font-12">
|
||||
使用
|
||||
<a href="https://2fas.com" target="_blank"><span class="layui-text layui-font-blue">2FAS</span></a>
|
||||
或者
|
||||
<a href="https://cn.bing.com/search?q=Google+Authenticator" target="_blank"><span class="layui-text layui-font-blue">Google Authenticator</span></a>
|
||||
APP 扫描二维码 后 输入验证码 进行绑定
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label required">谷歌验证码</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="ga_code" class="layui-input" maxlength="6" lay-verify="required" placeholder="扫描二维码,输入验证码" value="">
|
||||
</div>
|
||||
</div>
|
||||
<div class=" hr-line">
|
||||
</div>
|
||||
<div class="layui-form-item text-center">
|
||||
<button type="submit" class="layui-btn layui-btn-normal layui-btn-sm" lay-submit>确认</button>
|
||||
<button type="reset" class="layui-btn layui-btn-primary layui-btn-sm">重置</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
@@ -22,6 +22,11 @@
|
||||
<span class="bind-password icon icon-4"></span>
|
||||
</div>
|
||||
|
||||
<div class="item layui-hide" id="gaCode">
|
||||
<span class="icon icon-3"></span>
|
||||
<input type="text" name="ga_code" placeholder="谷歌验证码" maxlength="6">
|
||||
</div>
|
||||
|
||||
{if $captcha == 1}
|
||||
<div id="validatePanel" class="item" style="width: 137px;">
|
||||
<input type="text" name="captcha" placeholder="请输入验证码" maxlength="4">
|
||||
|
||||
@@ -37,7 +37,8 @@
|
||||
"qiniu/php-sdk": "v7.11.0",
|
||||
"ext-mysqli": "*",
|
||||
"ext-pdo": "*",
|
||||
"wolf-leo/phplogviewer": "^0.11.1"
|
||||
"wolf-leo/phplogviewer": "^0.11.3",
|
||||
"wolfcode/authenticator": "^0.0.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/var-dumper": ">=4.2",
|
||||
|
||||
@@ -97,6 +97,8 @@ CREATE TABLE `ea_system_admin`
|
||||
`create_time` int(11) DEFAULT NULL COMMENT '创建时间',
|
||||
`update_time` int(11) DEFAULT NULL COMMENT '更新时间',
|
||||
`delete_time` int(11) DEFAULT NULL COMMENT '删除时间',
|
||||
`login_type` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '登录方式',
|
||||
`ga_secret` varchar(32) NOT NULL DEFAULT '' COMMENT '谷歌验证码秘钥',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `username` (`username`) USING BTREE,
|
||||
KEY `phone` (`phone`)
|
||||
|
||||
@@ -164,10 +164,22 @@ define(["jquery", "easy-admin", "echarts", "echarts-theme", "miniAdmin", "miniTa
|
||||
})
|
||||
},
|
||||
editAdmin: function () {
|
||||
let form = layui.form
|
||||
form.on('radio(loginType-filter)', function (data) {
|
||||
let elem = data.elem
|
||||
let value = elem.value
|
||||
if (value === '2') {
|
||||
let width = screen.width < 768 ? '85%' : '60%'
|
||||
ea.open('绑定谷歌验证码', ea.url('index/set2fa'), width, '75%')
|
||||
}
|
||||
});
|
||||
ea.listen();
|
||||
},
|
||||
editPassword: function () {
|
||||
ea.listen();
|
||||
}
|
||||
},
|
||||
set2fa: function () {
|
||||
ea.listen();
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
@@ -39,6 +39,12 @@ define(["jquery", "easy-admin"], function ($, ea) {
|
||||
window.location = ea.url('index');
|
||||
})
|
||||
}, function (res) {
|
||||
let data = res.data
|
||||
if (data.is_ga_code) {
|
||||
let elem = $('#gaCode')
|
||||
elem.removeClass('layui-hide');
|
||||
elem.find('input').focus()
|
||||
}
|
||||
ea.msg.error(res.msg, function () {
|
||||
$('#refreshCaptcha').trigger("click");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user