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('没有操作权限');
    }
}

七、最佳实践建议

  1. 会话安全

    • 使用session_regenerate_id()防止会话固定攻击
    • 设置合理的session过期时间
    • 重要操作使用CSRF令牌
  2. 密码安全

    • 强制使用强密码策略
    • 定期要求修改密码
    • 记录密码修改历史
  3. 权限控制

    • 遵循最小权限原则
    • 实现RBAC权限模型
    • 记录所有敏感操作
  4. 登录保护

    • 实现登录失败锁定
    • 添加验证码机制
    • 记录登录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');
}

标签: PHP, 后端开发, ThinkPHP, ThinkPHP5, PHP安全, Web安全

添加新评论