自动化脚本如何清理未使用访问令牌

wen 实用脚本 24

本文目录导读:

自动化脚本如何清理未使用访问令牌

  1. 核心策略:基于过期时间和最后使用时间
  2. 方案一:基于数据库存储(最常见,例如 OAuth2.0/ 自建系统)
  3. 方案二:清理 Redis 缓存中的令牌(常见于微服务架构)
  4. 方案三:JWT 令牌(无状态,需依赖黑名单或撤销机制)
  5. 方案四:文件系统令牌(API Key 文件)
  6. 最佳实践建议
  7. 总结脚本框架

清理未使用的访问令牌(Access Token)是提升系统安全性和减少资源浪费的重要操作,具体实现方式取决于你的令牌存储方式(数据库、缓存、文件系统)以及令牌的发放机制(JWT、OAuth2.0、自签发)。

以下是针对不同场景的自动化脚本清理方案和核心逻辑:

核心策略:基于过期时间和最后使用时间

清理脚本的核心逻辑通常是双重过滤

  1. 已过期:令牌的 exp(过期时间)属性已过当前时间。
  2. 长期未使用:令牌未过期,但 last_used_at(最后使用时间)远早于当前时间(例如超过30天未使用)。

基于数据库存储(最常见,OAuth2.0/ 自建系统)

假设你有一个 access_tokens 表,结构类似:

CREATE TABLE access_tokens (
    id INT PRIMARY KEY,
    token_hash VARCHAR(64) UNIQUE,  -- 存储哈希,而非明文
    user_id INT,
    expires_at DATETIME,          -- 过期时间
    last_used_at DATETIME,        -- 最后使用时间
    is_revoked BOOLEAN DEFAULT FALSE
);

使用 SQL + 定时任务(Crontab / 计划任务)

#!/bin/bash
# 每天凌晨3点执行清理
# 定义清理策略:
# - 删除所有已过期的令牌
# - 删除超过30天未使用且已过期7天以上的令牌(双保险)
# - 更新 is_revoked = TRUE 而不是直接删除(软删除,保留审计)
mysql -u root -pYourPassword -e "
UPDATE access_tokens 
SET is_revoked = TRUE 
WHERE 
    (expires_at < NOW()) 
    OR (last_used_at < DATE_SUB(NOW(), INTERVAL 30 DAY));
"
# 可选:物理删除已标记为 revoked 超过 90 天的记录
# mysql -u root -pYourPassword -e "DELETE FROM access_tokens WHERE is_revoked = TRUE AND updated_at < DATE_SUB(NOW(), INTERVAL 90 DAY);"
echo "$(date): Token cleanup completed"

定时执行

# crontab -e
0 3 * * * /path/to/cleanup_tokens.sh

Python 脚本(更灵活,支持复杂日志和通知)

import datetime
import mysql.connector
import logging
logging.basicConfig(filename='token_cleanup.log', level=logging.INFO)
def cleanup_old_tokens():
    conn = mysql.connector.connect(user='root', password='pass', host='127.0.0.1', database='your_db')
    cursor = conn.cursor()
    now = datetime.datetime.now()
    thirty_days_ago = now - datetime.timedelta(days=30)
    # 1. 已过期
    query_expired = """
        UPDATE access_tokens SET is_revoked = TRUE 
        WHERE expires_at < %s AND is_revoked = FALSE
    """
    cursor.execute(query_expired, (now,))
    expired_count = cursor.rowcount
    # 2. 过期且超过30天未活跃(即使未过期也清理)
    query_inactive = """
        UPDATE access_tokens SET is_revoked = TRUE 
        WHERE (last_used_at < %s OR last_used_at IS NULL) 
        AND is_revoked = FALSE
    """
    cursor.execute(query_inactive, (thirty_days_ago,))
    inactive_count = cursor.rowcount
    conn.commit()
    cursor.close()
    conn.close()
    logging.info(f"Cleaned {expired_count} expired tokens and {inactive_count} inactive tokens.")
    print(f"Cleaned {expired_count} expired, {inactive_count} inactive.")
if __name__ == "__main__":
    cleanup_old_tokens()

优势:可以加入邮件/钉钉通知,记录每条被清理的令牌对应的 user_id 以便审计。


清理 Redis 缓存中的令牌(常见于微服务架构)

如果你的令牌存储在 Redis 中(例如作为 Session 或 JWT 黑名单),通常依赖于 TTL(存活时间),但 Redis 本身不记录“最后使用时间”,因此需要额外策略。

依赖 TTL 自动过期(推荐)

JWT 令牌本身包含过期时间,即使 Redis 未清理,只要解析令牌时校验 exp 即可。

# 无需脚本,Redis 会自动删除过期的 key
redis-cli config set key-limit 100000

清除不活跃的 Session(针对手动管理的 Redis Session)

如果你在 Redis 中存储了 user:session:{token} 并手动更新 last_access,可以扫描并清理:

import redis
import time
r = redis.Redis(host='localhost', port=6379, db=0)
def cleanup_inactive_sessions(inactive_seconds=2592000):  # 30天
    now = time.time()
    cursor = '0'
    deleted_count = 0
    while cursor != 0:
        cursor, keys = r.scan(cursor=cursor, match='user:session:*', count=1000)
        for key in keys:
            session_data = r.hgetall(key)
            if session_data:
                last_access = float(session_data.get(b'last_access', 0))
                if now - last_access > inactive_seconds:
                    r.delete(key)
                    deleted_count += 1
    print(f"Deleted {deleted_count} inactive sessions.")

注意:对于大型集群,扫描所有 key 可能影响性能,可以考虑使用 Redis 6+的 Redis TimeSeries额外维护一个活跃令牌的 Set


JWT 令牌(无状态,需依赖黑名单或撤销机制)

JWT 通常无法被服务端直接“删除”,因为它们是自包含的,清理的是撤销列表(Revocation List)黑名单缓存

  • 黑名单通常存储在 Redis 中,使用 令牌的 jti (JWT ID) 作为 key。
  • 清理脚本:删除黑名单中已过期的记录(因为 JWT 本身会过期,黑名单不再需要)。
# 清理 JWT 黑名单中已过期的条目
# 假设你的黑名单 key 是 "blacklist:jti:{jti}", 并且设置了与 JWT 相同的 TTL
# 不需要清理,Redis 会自动删除。

真正的“清理”是指: 定期删除数据库中的撤销记录(如果记录在数据库)。

DELETE FROM revoked_tokens WHERE expired_at < NOW();

文件系统令牌(API Key 文件)

如果你的令牌以 API Key 文件形式存在于服务器(不推荐,但存在老旧系统)。

#!/bin/bash
# 清理 /var/tokens/ 目录下超过 90 天未修改的令牌文件
find /var/tokens/ -type f -mtime +90 -delete
# 同时清理 0 字节的空文件
find /var/tokens/ -type f -empty -delete
echo "$(date): Cleaned old token files."

最佳实践建议

  1. 不可逆存储:数据库或日志中永远不要存储明文令牌,只存 SHA256 哈希。
  2. 软删除优先:先标记 is_revoked,保留 30-90 天再物理删除,以便审计和问题追溯。
  3. 分批清理:对于大型表(百万级),使用 LIMIT 1000 循环删除,避免锁表。
    -- 分批删除示例
    DELETE FROM access_tokens WHERE is_revoked = TRUE AND id > 0 ORDER BY id LIMIT 1000;
  4. 监控与告警:记录每次清理的数量,如果某次清理数量异常多(例如突然清理了 10 万个),触发告警。
  5. 考虑短生命周期令牌:如果业务允许,将令牌过期时间设为 15 分钟(Access Token),配合 Refresh Token(7 天),这样可以大幅减少清理压力。

总结脚本框架

存储方式 清理命令/脚本 频率
MySQL/PostgreSQL UPDATE ... WHERE expires_at < NOW() OR last_used_at < ... + Crontab 每天一次
Redis 依赖 TTL 自动
JWT 黑名单 依赖 TTL + 定期清理已过期的 revoked_tokens 每天一次
文件系统 find + -mtime 每周一次

你可以根据你的实际存储方案,选择上述最匹配的脚本模板并修改表名、字段和清理阈值即可。

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