增强后台安全限制

优化登录判断机制
移除冗余代码
pull/446/head
Karson 2023-06-20 11:51:59 +08:00
parent a40420b8cd
commit 7e6314a701
6 changed files with 92 additions and 33 deletions

View File

@ -70,6 +70,8 @@ class Index extends Backend
if ($this->auth->isLogin()) { if ($this->auth->isLogin()) {
$this->success(__("You've logged in, do not login again"), $url); $this->success(__("You've logged in, do not login again"), $url);
} }
//保持会话有效时长,单位:小时
$keeyloginhours = 24;
if ($this->request->isPost()) { if ($this->request->isPost()) {
$username = $this->request->post('username'); $username = $this->request->post('username');
$password = $this->request->post('password'); $password = $this->request->post('password');
@ -95,7 +97,7 @@ class Index extends Backend
$this->error($validate->getError(), $url, ['token' => $this->request->token()]); $this->error($validate->getError(), $url, ['token' => $this->request->token()]);
} }
AdminLog::setTitle(__('Login')); AdminLog::setTitle(__('Login'));
$result = $this->auth->login($username, $password, $keeplogin ? 86400 : 0); $result = $this->auth->login($username, $password, $keeplogin ? $keeyloginhours * 3600 : 0);
if ($result === true) { if ($result === true) {
Hook::listen("admin_login_after", $this->request); Hook::listen("admin_login_after", $this->request);
$this->success(__('Login successful'), $url, ['url' => $url, 'id' => $this->auth->id, 'username' => $username, 'avatar' => $this->auth->avatar]); $this->success(__('Login successful'), $url, ['url' => $url, 'id' => $this->auth->id, 'username' => $username, 'avatar' => $this->auth->avatar]);
@ -113,6 +115,7 @@ class Index extends Backend
} }
$background = Config::get('fastadmin.login_background'); $background = Config::get('fastadmin.login_background');
$background = $background ? (stripos($background, 'http') === 0 ? $background : config('site.cdnurl') . $background) : ''; $background = $background ? (stripos($background, 'http') === 0 ? $background : config('site.cdnurl') . $background) : '';
$this->view->assign('keeyloginhours', $keeyloginhours);
$this->view->assign('background', $background); $this->view->assign('background', $background);
$this->view->assign('title', __('Login')); $this->view->assign('title', __('Login'));
Hook::listen("admin_login_init", $this->request); Hook::listen("admin_login_init", $this->request);

View File

@ -129,7 +129,7 @@ class Admin extends Backend
exception(__("Please input correct password")); exception(__("Please input correct password"));
} }
$params['salt'] = Random::alnum(); $params['salt'] = Random::alnum();
$params['password'] = md5(md5($params['password']) . $params['salt']); $params['password'] = $this->auth->getEncryptPassword($params['password'], $params['salt']);
$params['avatar'] = '/assets/img/avatar.png'; //设置新管理员默认头像。 $params['avatar'] = '/assets/img/avatar.png'; //设置新管理员默认头像。
$result = $this->model->validate('Admin.add')->save($params); $result = $this->model->validate('Admin.add')->save($params);
if ($result === false) { if ($result === false) {
@ -183,7 +183,7 @@ class Admin extends Backend
exception(__("Please input correct password")); exception(__("Please input correct password"));
} }
$params['salt'] = Random::alnum(); $params['salt'] = Random::alnum();
$params['password'] = md5(md5($params['password']) . $params['salt']); $params['password'] = $this->auth->getEncryptPassword($params['password'], $params['salt']);
} else { } else {
unset($params['password'], $params['salt']); unset($params['password'], $params['salt']);
} }
@ -192,7 +192,7 @@ class Admin extends Backend
$adminValidate->rule([ $adminValidate->rule([
'username' => 'require|regex:\w{3,30}|unique:admin,username,' . $row->id, 'username' => 'require|regex:\w{3,30}|unique:admin,username,' . $row->id,
'email' => 'require|email|unique:admin,email,' . $row->id, 'email' => 'require|email|unique:admin,email,' . $row->id,
'mobile' => 'regex:1[3-9]\d{9}|unique:admin,mobile,' . $row->id, 'mobile' => 'regex:1[3-9]\d{9}|unique:admin,mobile,' . $row->id,
'password' => 'regex:\S{32}', 'password' => 'regex:\S{32}',
]); ]);
$result = $row->validate('Admin.edit')->save($params); $result = $row->validate('Admin.edit')->save($params);

View File

@ -60,5 +60,6 @@ return [
'Forum' => '交流社区', 'Forum' => '交流社区',
'QQ qun' => 'QQ交流群', 'QQ qun' => 'QQ交流群',
'Captcha' => '验证码', 'Captcha' => '验证码',
'The duration of the session is %s hours' => '设定会话有效时长为%s小时',
'Security tips' => '<i class="fa fa-warning"></i> 安全提示为了你的后台安全请勿将后台管理入口设置为admin或admin.php', 'Security tips' => '<i class="fa fa-warning"></i> 安全提示为了你的后台安全请勿将后台管理入口设置为admin或admin.php',
]; ];

View File

@ -51,7 +51,7 @@ class Auth extends \fast\Auth
$this->setError('Please try again after 1 day'); $this->setError('Please try again after 1 day');
return false; return false;
} }
if ($admin->password != md5(md5($password) . $admin->salt)) { if ($admin->password != $this->getEncryptPassword($password, $admin->salt)) {
$admin->loginfailure++; $admin->loginfailure++;
$admin->save(); $admin->save();
$this->setError('Password is incorrect'); $this->setError('Password is incorrect');
@ -63,7 +63,8 @@ class Auth extends \fast\Auth
$admin->token = Random::uuid(); $admin->token = Random::uuid();
$admin->save(); $admin->save();
Session::set("admin", $admin->toArray()); Session::set("admin", $admin->toArray());
$this->keeplogin($keeptime); Session::set("admin.safecode", $this->getEncryptSafecode($admin));
$this->keeplogin($admin, $keeptime);
return true; return true;
} }
@ -101,7 +102,7 @@ class Auth extends \fast\Auth
return false; return false;
} }
//token有变更 //token有变更
if ($key != md5(md5($id) . md5($keeptime) . md5($expiretime) . $admin->token . config('token.key'))) { if ($key != $this->getKeeploginKey($admin, $keeptime, $expiretime)) {
return false; return false;
} }
$ip = request()->ip(); $ip = request()->ip();
@ -110,8 +111,9 @@ class Auth extends \fast\Auth
return false; return false;
} }
Session::set("admin", $admin->toArray()); Session::set("admin", $admin->toArray());
Session::set("admin.safecode", $this->getEncryptSafecode($admin));
//刷新自动登录的时效 //刷新自动登录的时效
$this->keeplogin($keeptime); $this->keeplogin($admin, $keeptime);
return true; return true;
} else { } else {
return false; return false;
@ -124,18 +126,64 @@ class Auth extends \fast\Auth
* @param int $keeptime * @param int $keeptime
* @return boolean * @return boolean
*/ */
protected function keeplogin($keeptime = 0) protected function keeplogin($admin, $keeptime = 0)
{ {
if ($keeptime) { if ($keeptime) {
$expiretime = time() + $keeptime; $expiretime = time() + $keeptime;
$key = md5(md5($this->id) . md5($keeptime) . md5($expiretime) . $this->token . config('token.key')); $key = $this->getKeeploginKey($admin, $keeptime, $expiretime);
$data = [$this->id, $keeptime, $expiretime, $key]; Cookie::set('keeplogin', implode('|', [$admin['id'], $keeptime, $expiretime, $key]), $keeptime);
Cookie::set('keeplogin', implode('|', $data), 86400 * 7);
return true; return true;
} }
return false; return false;
} }
/**
* 获取密码加密后的字符串
* @param string $password 密码
* @param string $salt 密码盐
* @return string
*/
public function getEncryptPassword($password, $salt = '')
{
return md5(md5($password) . $salt);
}
/**
* 获取密码加密后的自动登录码
* @param string $password 密码
* @param string $salt 密码盐
* @return string
*/
public function getEncryptKeeplogin($params, $keeptime)
{
$expiretime = time() + $keeptime;
$key = md5(md5($params['id']) . md5($keeptime) . md5($expiretime) . $params['token'] . config('token.key'));
return implode('|', [$this->id, $keeptime, $expiretime, $key]);
}
/**
* 获取自动登录Key
* @param $params
* @param $keeptime
* @param $expiretime
* @return string
*/
public function getKeeploginKey($params, $keeptime, $expiretime)
{
$key = md5(md5($params['id']) . md5($keeptime) . md5($expiretime) . $params['token'] . config('token.key'));
return $key;
}
/**
* 获取加密后的安全码
* @param $params
* @return string
*/
public function getEncryptSafecode($params)
{
return md5(md5($params['username']) . md5(substr($params['password'], 0, 6)) . config('token.key'));
}
public function check($name, $uid = '', $relation = 'or', $mode = 'url') public function check($name, $uid = '', $relation = 'or', $mode = 'url')
{ {
$uid = $uid ? $uid : $this->id; $uid = $uid ? $uid : $this->id;
@ -180,13 +228,19 @@ class Auth extends \fast\Auth
if (!$admin) { if (!$admin) {
return false; return false;
} }
$my = Admin::get($admin['id']);
if (!$my) {
return false;
}
//校验安全码,可用于判断关键信息发生了变更需要重新登录
if (!isset($admin['safecode']) || $this->getEncryptSafecode($my) !== $admin['safecode']) {
$this->logout();
return false;
}
//判断是否同一时间同一账号只能在一个地方登录 //判断是否同一时间同一账号只能在一个地方登录
if (Config::get('fastadmin.login_unique')) { if (Config::get('fastadmin.login_unique')) {
$my = Admin::get($admin['id']); if ($my['token'] != $admin['token']) {
if (!$my || $my['token'] != $admin['token']) { $this->logout();
$this->logined = false; //重置登录状态
Session::delete("admin");
Cookie::delete("keeplogin");
return false; return false;
} }
} }

View File

@ -13,22 +13,20 @@ class Admin extends Model
// 定义时间戳字段名 // 定义时间戳字段名
protected $createTime = 'createtime'; protected $createTime = 'createtime';
protected $updateTime = 'updatetime'; protected $updateTime = 'updatetime';
protected $hidden = [
'password',
'salt'
];
/** public static function init()
* 重置用户密码
* @author baiyouwen
*/
public function resetPassword($uid, $NewPassword)
{ {
$passwd = $this->encryptPassword($NewPassword); self::beforeWrite(function ($row) {
$ret = $this->where(['id' => $uid])->update(['password' => $passwd]); $changed = $row->getChangedData();
return $ret; //如果修改了用户或或密码则需要重新登录
} if (isset($changed['username']) || isset($changed['password']) || isset($changed['salt'])) {
$row->token = '';
// 密码加密 }
protected function encryptPassword($password, $salt = '', $encrypt = 'md5') });
{
return $encrypt($password . $salt);
} }
} }

View File

@ -28,7 +28,7 @@
box-shadow: 0 0 30px rgba(0, 0, 0, 0.1); box-shadow: 0 0 30px rgba(0, 0, 0, 0.1);
background: rgba(255, 255, 255, 1); background: rgba(255, 255, 255, 1);
border: none; border: none;
overflow: hidden; /*overflow: hidden;*/
padding: 0; padding: 0;
} }
@ -55,6 +55,7 @@
.login-head { .login-head {
background: #899fe1; background: #899fe1;
border-radius: 3px 3px 0 0;
} }
.login-form { .login-form {
@ -122,12 +123,14 @@
</div> </div>
{/if} {/if}
<!--@CaptchaEnd--> <!--@CaptchaEnd-->
{if $keeyloginhours>0}
<div class="form-group checkbox"> <div class="form-group checkbox">
<label class="inline" for="keeplogin"> <label class="inline" for="keeplogin" data-toggle="tooltip" title="{:__('The duration of the session is %s hours', $keeyloginhours)}">
<input type="checkbox" name="keeplogin" id="keeplogin" value="1"/> <input type="checkbox" name="keeplogin" id="keeplogin" value="1"/>
{:__('Keep login')} {:__('Keep login')}
</label> </label>
</div> </div>
{/if}
<div class="form-group"> <div class="form-group">
<button type="submit" class="btn btn-success btn-lg btn-block" style="background:#708eea;">{:__('Sign in')}</button> <button type="submit" class="btn btn-success btn-lg btn-block" style="background:#708eea;">{:__('Sign in')}</button>
</div> </div>