怎样在PHP项目中实现用户自定义过期?

wen java案例 3

本文目录导读:

怎样在PHP项目中实现用户自定义过期?

  1. 基于数据库字段(最常用)
  2. 使用Session + 数据库(临时过期更灵活)
  3. 基于JWT Token(适合API接口)
  4. Redis缓存实现(高性能场景)
  5. 完整项目集成示例
  6. 推荐方案选择

在PHP项目中实现用户自定义过期时间,常见有以下几种方案,我按推荐程度排序介绍:

基于数据库字段(最常用)

实现方式

// 用户表增加过期字段
CREATE TABLE users (
    id INT PRIMARY KEY,
    username VARCHAR(50),
    email VARCHAR(100),
    expire_at DATETIME,        -- 过期时间
    is_active TINYINT DEFAULT 1
);
// 创建用户时设置过期时间
function createUserWithExpire($username, $email, $expireDays = 30) {
    $expireAt = date('Y-m-d H:i:s', strtotime("+{$expireDays} days"));
    $sql = "INSERT INTO users (username, email, expire_at) 
            VALUES (?, ?, ?)";
    // 使用PDO或mysqli执行
}
// 验证用户是否过期
function checkUserExpired($userId) {
    $sql = "SELECT expire_at FROM users WHERE id = ?";
    $user = $db->fetchOne($sql, [$userId]);
    if (!$user) return true;
    return strtotime($user['expire_at']) < time();
}
// 每次请求时检查
function authMiddleware() {
    if (checkUserExpired($_SESSION['user_id'])) {
        // 用户已过期,跳转到过期页面或禁止访问
        header('Location: /expired.php');
        exit;
    }
}

使用Session + 数据库(临时过期更灵活)

// 登录时设置session过期
session_start();
function loginWithCustomExpire($userId, $expireMinutes = 30) {
    // 数据库记录
    $sql = "UPDATE users SET session_expire_at = ? WHERE id = ?";
    $expireAt = date('Y-m-d H:i:s', time() + $expireMinutes * 60);
    // Session设置
    $_SESSION['user_id'] = $userId;
    $_SESSION['expire_at'] = $expireAt;
    $_SESSION['login_time'] = time();
}
// 中间件检查
function checkSessionExpired() {
    if (!isset($_SESSION['expire_at'])) {
        return false;
    }
    if (strtotime($_SESSION['expire_at']) < time()) {
        // 过期,销毁session
        session_destroy();
        return true;
    }
    // 可选:每次访问更新过期时间(滑动过期)
    $slidingExpire = true;
    if ($slidingExpire) {
        $_SESSION['expire_at'] = date('Y-m-d H:i:s', 
            time() + 30 * 60); // 再延长30分钟
    }
    return false;
}

基于JWT Token(适合API接口)

// 使用 firebase/php-jwt 库
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
class CustomJWT {
    private $secretKey;
    public function __construct($secretKey) {
        $this->secretKey = $secretKey;
    }
    // 生成带自定义过期时间的Token
    public function generateToken($userId, $expireHours = 24) {
        $payload = [
            'iss' => 'your-app',        // 签发者
            'iat' => time(),            // 签发时间
            'exp' => time() + $expireHours * 3600,  // 过期时间
            'user_id' => $userId,
            'custom_data' => [
                'role' => 'premium',
                'max_requests' => 1000
            ]
        ];
        return JWT::encode($payload, $this->secretKey, 'HS256');
    }
    // 验证Token
    public function validateToken($token) {
        try {
            $decoded = JWT::decode($token, new Key($this->secretKey, 'HS256'));
            // 检查自定义过期条件
            if ($decoded->custom_data->max_requests < 0) {
                return false;  // 请求次数用完也算过期
            }
            return (array)$decoded;
        } catch (\Exception $e) {
            // Token过期或无效
            return false;
        }
    }
}
// 使用示例
$jwt = new CustomJWT('your-secret-key-here');
$token = $jwt->generateToken(123, 48);  // 48小时过期

Redis缓存实现(高性能场景)

class RedisExpireManager {
    private $redis;
    public function __construct($redis) {
        $this->redis = $redis;
    }
    // 设置用户自定义过期
    public function setUserExpire($userId, $ttlSeconds) {
        $key = "user:{$userId}:expire";
        // 存储过期时间戳
        $this->redis->setex($key, $ttlSeconds, time() + $ttlSeconds);
        // 也可以构建用户会话
        $sessionKey = "user:{$userId}:session";
        $sessionData = json_encode([
            'user_id' => $userId,
            'expire_at' => time() + $ttlSeconds,
            'last_activity' => time()
        ]);
        $this->redis->setex($sessionKey, $ttlSeconds, $sessionData);
    }
    // 检查是否过期
    public function isExpired($userId) {
        $key = "user:{$userId}:expire";
        $expireAt = $this->redis->get($key);
        if (!$expireAt) return true;  // 不存在视为过期
        return $expireAt < time();
    }
    // 更新过期时间(滑动窗口)
    public function refreshExpire($userId, $additionalSeconds = 3600) {
        $key = "user:{$userId}:expire";
        if ($this->redis->exists($key)) {
            // 重新设置过期时间
            $this->redis->expire($key, $additionalSeconds);
        }
    }
}

完整项目集成示例

<?php
// UserAuth.php - 用户认证类
class UserAuth {
    private $db;
    private $expireStrategies = [];
    public function __construct($db) {
        $this->db = $db;
    }
    // 注册过期策略
    public function addExpireStrategy($name, callable $checker) {
        $this->expireStrategies[$name] = $checker;
    }
    // 用户登录
    public function login($username, $password, $expireConfig = []) {
        // 验证用户...
        // 设置过期
        $expireConfig = array_merge([
            'type' => 'fixed',      // fixed, sliding, hybrid
            'duration' => 3600,     // 1小时
            'max_idle' => 1800      // 最大空闲时间
        ], $expireConfig);
        // 存储过期配置
        $_SESSION['expire_config'] = $expireConfig;
        $_SESSION['login_time'] = time();
        $_SESSION['last_activity'] = time();
        $_SESSION['user_id'] = $userId;
        return true;
    }
    // 检查过期
    public function checkExpired() {
        if (!isset($_SESSION['expire_config'])) {
            return false;
        }
        $config = $_SESSION['expire_config'];
        $now = time();
        switch ($config['type']) {
            case 'fixed':
                // 固定过期时间
                return ($_SESSION['login_time'] + $config['duration']) < $now;
            case 'sliding':
                // 滑动过期:基于最后活动时间
                $isExpired = ($_SESSION['last_activity'] + $config['duration']) < $now;
                if (!$isExpired) {
                    $_SESSION['last_activity'] = $now; // 更新活动时间
                }
                return $isExpired;
            case 'hybrid':
                // 混合模式:总时长+空闲超时
                $totalExpired = ($_SESSION['login_time'] + $config['duration']) < $now;
                $idleExpired = ($_SESSION['last_activity'] + $config['max_idle']) < $now;
                if (!$idleExpired) {
                    $_SESSION['last_activity'] = $now;
                }
                return $totalExpired || $idleExpired;
        }
        return false;
    }
    // 用户自定义过期查询
    public function getUserCustomExpire($userId) {
        // 从数据库获取用户的个性化过期设置
        $sql = "SELECT expire_type, expire_value, expire_unit 
                FROM user_expire_config 
                WHERE user_id = ?";
        $config = $this->db->fetchOne($sql, [$userId]);
        if (!$config) {
            // 默认过期设置
            return [
                'type' => 'fixed',
                'value' => 24,
                'unit' => 'hours'
            ];
        }
        // 根据配置计算过期时间
        switch ($config['unit']) {
            case 'minutes':
                $expireSeconds = $config['expire_value'] * 60;
                break;
            case 'hours':
                $expireSeconds = $config['expire_value'] * 3600;
                break;
            case 'days':
                $expireSeconds = $config['expire_value'] * 86400;
                break;
            case 'custom':
                // 自定义逻辑
                $expireSeconds = $this->calculateCustomExpire($config);
                break;
        }
        return [
            'type' => $config['expire_type'],
            'duration' => $expireSeconds
        ];
    }
    private function calculateCustomExpire($config) {
        // 复杂的自定义过期逻辑
        // 周末不过期、特定时间段过期等
        $now = new DateTime();
        $weekDay = $now->format('N');
        if ($weekDay >= 6) {  // 周末
            return 86400 * 2;  // 2天
        }
        return 3600;  // 工作日1小时
    }
}
// 使用示例
$auth = new UserAuth($db);
// 注册自定义过期检查策略
$auth->addExpireStrategy('weekend_free', function($userId) {
    $now = new DateTime();
    return $now->format('N') < 6;  // 非周末返回false(不过期)
});
$auth->addExpireStrategy('usage_limit', function($userId) {
    // 检查API调用次数
    return getApiUsage($userId) > 1000;
});
// 用户登录时设置自定义过期
$expireConfig = $auth->getUserCustomExpire($userId);
$auth->login($username, $password, $expireConfig);

推荐方案选择

场景 推荐方案 原因
简单网站 数据库字段 实现简单,易于维护
REST API JWT Token 无状态,适合分布式
高并发 Redis 性能好,支持过期通知
复杂权限 数据库+自定义策略 灵活可扩展

选择哪种方案取决于你的项目规模、并发量和业务复杂度,对于大多数中小型项目,数据库字段方案就足够了。

抱歉,评论功能暂时关闭!