ThinkPHP5 用户登录状态检测与权限控制完整指南
ThinkPHP5 用户登录状态检测与权限控制完整指南
一、基础登录验证实现
1.1 公共控制器基类 (Common.php)
<?php
namespace app\admin\controller;
use think\Controller;
use think\Session;
class Common extends Controller
{
// 初始化方法(自动调用)
public function _initialize()
{
// 检查session中的用户标识
if (!Session::has('admin_id')) {
// 未登录跳转到登录页
$this->redirect('admin/login/index');
}
// 可选:验证用户状态是否有效
$this->checkUserStatus();
}
// 验证用户状态
protected function checkUserStatus()
{
$adminId = Session::get('admin_id');
$user = \app\admin\model\Admin::get($adminId);
if (!$user || $user->status != 1) {
Session::delete('admin_id');
$this->error('账户已被禁用或不存在', 'admin/login/index');
}
}
}
二、登录控制器实现 (Login.php)
<?php
namespace app\admin\controller;
use think\Controller;
use think\Session;
use app\admin\model\Admin;
class Login extends Controller
{
// 登录页面
public function index()
{
// 已登录直接跳转
if (Session::has('admin_id')) {
$this->redirect('admin/index/index');
}
return $this->fetch();
}
// 登录处理
public function doLogin()
{
$username = $this->request->post('username');
$password = $this->request->post('password');
// 验证规则
$validate = new \think\Validate([
'username|用户名' => 'require|length:4,20',
'password|密码' => 'require|length:6,20'
]);
if (!$validate->check(['username'=>$username, 'password'=>$password])) {
$this->error($validate->getError());
}
// 查询用户
$user = Admin::where('username', $username)
->where('status', 1)
->find();
// 密码验证(建议使用password_hash加密)
if ($user && password_verify($password, $user->password)) {
// 记录登录信息
Session::set('admin_id', $user->id);
Session::set('admin_name', $user->username);
// 更新登录信息
$user->last_login_time = time();
$user->last_login_ip = $this->request->ip();
$user->save();
$this->success('登录成功', 'admin/index/index');
} else {
$this->error('用户名或密码错误');
}
}
// 退出登录
public function logout()
{
Session::clear();
$this->success('退出成功', 'admin/login/index');
}
}
三、权限控制器示例 (Index.php)
<?php
namespace app\admin\controller;
class Index extends Common
{
// 后台首页
public function index()
{
// 获取当前管理员信息
$adminId = Session::get('admin_id');
$adminInfo = \app\admin\model\Admin::get($adminId);
// 获取菜单权限
$menu = $this->getAdminMenu($adminId);
$this->assign([
'admin' => $adminInfo,
'menu' => $menu
]);
return $this->fetch();
}
// 获取管理员菜单
protected function getAdminMenu($adminId)
{
// 如果是超级管理员
if ($adminId == 1) {
return \app\admin\model\Menu::where('status', 1)
->order('sort ASC')
->select();
}
// 普通管理员获取有权限的菜单
$auth = new \app\admin\service\Auth();
return $auth->getAuthMenu($adminId);
}
}
四、数据库设计建议
4.1 管理员表 (admin)
CREATE TABLE `admin` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(20) NOT NULL COMMENT '用户名',
`password` varchar(255) NOT NULL COMMENT '密码',
`salt` varchar(10) DEFAULT NULL COMMENT '密码盐',
`avatar` varchar(100) DEFAULT NULL COMMENT '头像',
`email` varchar(50) DEFAULT NULL COMMENT '邮箱',
`mobile` varchar(11) DEFAULT NULL COMMENT '手机号',
`status` tinyint(1) DEFAULT '1' COMMENT '状态(0:禁用,1:启用)',
`last_login_time` int(11) DEFAULT NULL COMMENT '最后登录时间',
`last_login_ip` varchar(50) DEFAULT NULL COMMENT '最后登录IP',
`create_time` int(11) DEFAULT NULL COMMENT '创建时间',
`update_time` int(11) DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='管理员表';
4.2 权限规则表 (auth_rule)
CREATE TABLE `auth_rule` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`pid` int(11) DEFAULT '0' COMMENT '父ID',
`name` varchar(50) DEFAULT NULL COMMENT '规则唯一标识',
`title` varchar(50) DEFAULT NULL COMMENT '规则名称',
`icon` varchar(50) DEFAULT NULL COMMENT '图标',
`type` tinyint(1) DEFAULT '1' COMMENT '类型(1:菜单,2:权限)',
`condition` varchar(100) DEFAULT NULL COMMENT '条件',
`status` tinyint(1) DEFAULT '1' COMMENT '状态(0:禁用,1:启用)',
`sort` int(11) DEFAULT '0' COMMENT '排序',
`create_time` int(11) DEFAULT NULL COMMENT '创建时间',
`update_time` int(11) DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='权限规则表';
五、增强安全措施
5.1 密码加密处理
// 生成密码
$salt = rand(1000, 9999);
$password = password_hash($password.$salt, PASSWORD_BCRYPT);
// 验证密码
if (password_verify($inputPassword.$user['salt'], $user['password'])) {
// 验证通过
}
5.2 操作日志记录
// 在Common控制器中添加
protected function writeLog($action, $content)
{
\app\admin\model\Log::create([
'admin_id' => Session::get('admin_id'),
'username' => Session::get('admin_name'),
'url' => $this->request->url(),
'title' => $action,
'content' => $content,
'ip' => $this->request->ip(),
'useragent'=> $this->request->server('HTTP_USER_AGENT')
]);
}
// 使用示例
$this->writeLog('登录系统', '管理员登录后台');
六、多层级权限验证
6.1 权限验证服务 (Auth.php)
<?php
namespace app\admin\service;
use think\Db;
class Auth
{
// 检查权限
public function check($name, $adminId)
{
// 超级管理员拥有所有权限
if ($adminId == 1) {
return true;
}
// 获取用户所有权限规则
$rules = $this->getAuthRules($adminId);
// 检查当前请求是否在权限规则中
$request = request();
$ruleName = strtolower($request->module().'/'.$request->controller().'/'.$request->action());
return in_array($ruleName, $rules);
}
// 获取用户权限规则
protected function getAuthRules($adminId)
{
// 获取用户组
$groups = Db::name('auth_group_access')
->where('admin_id', $adminId)
->column('group_id');
if (empty($groups)) {
return [];
}
// 获取权限规则
$rules = Db::name('auth_group')
->where('id', 'in', $groups)
->column('rules');
$rules = implode(',', $rules);
return array_unique(explode(',', $rules));
}
}
6.2 在控制器中使用
// 在Common控制器中增加权限验证
protected function checkAuth()
{
$auth = new \app\admin\service\Auth();
$adminId = Session::get('admin_id');
if (!$auth->check(null, $adminId)) {
$this->error('没有操作权限');
}
}
七、最佳实践建议
-
会话安全:
- 使用
session_regenerate_id()
防止会话固定攻击 - 设置合理的session过期时间
- 重要操作使用CSRF令牌
- 使用
-
密码安全:
- 强制使用强密码策略
- 定期要求修改密码
- 记录密码修改历史
-
权限控制:
- 遵循最小权限原则
- 实现RBAC权限模型
- 记录所有敏感操作
-
登录保护:
- 实现登录失败锁定
- 添加验证码机制
- 记录登录IP和设备信息
八、常见问题解决方案
Q1: 如何实现记住登录功能?
解决方案:
// 登录成功后设置cookie
$rememberToken = md5(uniqid(rand(), true));
$expire = time() + 86400 * 7; // 7天有效期
// 保存token到数据库
$user->remember_token = $rememberToken;
$user->save();
// 设置cookie
cookie('remember_token', $rememberToken, $expire);
// 自动登录检查
if (!Session::has('admin_id') && cookie('remember_token')) {
$user = Admin::where('remember_token', cookie('remember_token'))->find();
if ($user) {
Session::set('admin_id', $user->id);
}
}
Q2: 如何防止暴力破解?
解决方案:
// 记录登录失败次数
$failKey = 'login_fail_'.$username;
$failCount = cache($failKey) ?: 0;
if ($failCount >= 5) {
$this->error('尝试次数过多,请15分钟后再试');
}
if (!$user || !password_verify($password, $user->password)) {
$failCount++;
cache($failKey, $failCount, 900); // 15分钟缓存
$this->error('用户名或密码错误');
}
// 登录成功后清除计数
cache($failKey, null);
Q3: 如何实现多设备登录控制?
解决方案:
// 登录时生成唯一token
$loginToken = md5(uniqid(rand(), true));
Session::set('login_token', $loginToken);
$user->login_token = $loginToken;
$user->save();
// 每次请求验证token
if (Session::get('login_token') != $user->login_token) {
Session::clear();
$this->error('账号已在其他地方登录', 'admin/login/index');
}