首页 > 投稿 > 正文内容

PHP生成带过期时间的Token实现教程(附代码)

投稿2025-05-27 20:04:56

你做的登录功能是不是总被老板骂不安全?用户刚登录半小时就被盗号?八成是没给Token加"保质期"!今天咱们就用煮泡面的功夫,教你搞出带过期时间的Token,保准连黑客都直摇头!


一、为什么Token要有保质期?

想象一下你家大门钥匙永远不换,是不是慌得睡不着?Token过期时间就是给钥匙加个自动销毁程序。??三大核心作用??:

  1. ??降低风险??:就算被盗,有效期过了也白搭
  2. ??强制更新??:定期更换加密规则
  3. ??清理内存??:自动释放服务器存储空间

??反面教材??:

  • 某社交APP的7天免登录 → 被盗号者玩成永久权限
  • 某电商平台Token永不过期 → 被黑产批量倒卖
  • 某政府系统用session_id当Token → 分分钟被破解

二、四种实现方式任你选

▍ 方法1:时间戳+随机数(新手村装备)

php复制
$token = md5(time() . bin2hex(random_bytes(16)));
$expire = time() + 7200; // 2小时后过期

??适合场景??:临时文件下载、验证码等低安全需求

▍ 方法2:数据库记录法(经典款)

php复制
// 生成
$token = bin2hex(random_bytes(32));
$expires_at = date('Y-m-d H:i:s', time() + 3600);

// 存数据库
$db->query("INSERT INTO tokens (token, user_id, expires_at) 
           VALUES ('$token', 123, '$expires_at')");

??查过期技巧??:

php复制
SELECT token FROM tokens WHERE expires_at > NOW()

▍ 方法3:JWT自带过期(高端玩家必备)

php复制
use Firebase\JWT\JWT;

$payload = [
    'user_id' => 123,
    'exp' => time() + 1800 // 重点在这里!
];
$token = JWT::encode($payload, '你的密钥', 'HS256');

▍ 方法4:加密饼干法(Cookie流派)

php复制
$data = [
    'user_id' => 123,
    'exp' => time() + 86400 // 24小时
];
$token = base64_encode(
    openssl_encrypt(
        json_encode($data),
        'AES-256-CBC',
        '密钥至少32位',
        0,
        '初始向量也要16位'
    )
);

三、过期时间设置的五大黄金法则

  1. ??长短搭配更安全??

    场景推荐时长理由
    移动端APP7-30天减少重复登录
    后台管理系统2-4小时敏感操作多
    支付流程5-15分钟资金安全
    验证链接30分钟平衡安全与用户体验
  2. ??动态调整更智能??

    • 用户保持活跃时自动续期
    • 异地登录立即失效
    • 修改密码后所有Token过期
  3. ??服务器时间要校准??
    遇到过期的坑?检查这三处:

    • PHP时区设置 date_default_timezone_set('Asia/Shanghai')
    • 数据库服务器的系统时间
    • Nginx/Apache的时区配置
  4. ??容错时间窗??
    加个缓冲期,防止时间不同步:

    php复制
    $isValid = ($token_expire_time > time() - 300); // 允许5分钟误差
  5. ??多端不同步处理??
    手机、电脑、平板同时登录?给每个设备发独立Token!


四、代码实战:带自动续期的Token

php复制
function generateToken($userId) {
    $secret = '你的密钥应该比银行卡密码还复杂';
    $expire = 3600; // 基础有效期1小时
    
    // 根据用户等级延长有效期
    if ($user->isVIP()) {
        $expire = 2592000; // 30天
    }
    
    $payload = [
        'iat' => time(),
        'exp' => time() + $expire,
        'user_id' => $userId,
        'version' => '2023_token_v2' // 密钥更新时让旧Token集体失效
    ];
    
    return hash_hmac('sha256', json_encode($payload), $secret);
}

// 检查是否过期
function checkToken($token) {
    // 解密逻辑...
    if ($payload['exp'] < time()) {
        // 自动续期条件:7天内登录过且不是重要操作
        if ($payload['version'] == CURRENT_TOKEN_VERSION 
            && time() - $payload['iat'] < 604800) {
            return generateToken($payload['user_id']);
        }
        throw new Exception('Token已过期');
    }
}

五、避坑指南:血泪经验大放送

  1. ??千万别用客户端时间??
    有次前端小哥用JavaScript生成过期时间,结果用户修改系统时间就永久有效了!

  2. ??数据库索引要到位??
    曾经因为没给expires_at字段加索引,百万级数据查询直接卡死

  3. ??密钥管理三不要??

    • 不要写死在代码里
    • 不要用Github免费搜索能找到的密钥
    • 不要所有环境用同一套密钥
  4. ??日志记得打码??
    见过有程序员把完整Token写进日志文件,结果被运维导出泄露

  5. ??定期清理过期Token??
    用cron定时任务每天凌晨3点删过期数据:

    sql复制
    DELETE FROM tokens WHERE expires_at < NOW()

说点得罪人的大实话

Token过期时间这玩意儿,就像汽车的刹车系统。用时间戳+随机数相当于手刹,JWT方案是ABS防抱死系统,自定义加密那是赛车级碳陶刹车盘。但甭管用啥,最重要的是——别以为自己写了个完美方案就高枕无忧!

去年我帮朋友审计代码,发现他用的加密算法组合比俄罗斯套娃还复杂,结果密钥居然用公司名字+成立年份,黑客三分钟就破解了。所以啊,安全这回事,往往不是技术不够高级,而是基础操作没做到位。

最后送大家一句话:Token的过期时间不是越长越好,也不是越短越安全。就像煮泡面,3分钟太硬,5分钟太烂,找到最适合自己业务的"黄金4分钟"才是王道!

搜索