本文目录导读:

- 方案一:基于数据库的客户端ID清理(通用SQL + Shell脚本)
- 方案二:基于日志分析(ELK/Grafana + Python)
- 方案三:Kubernetes环境清理过期的ServiceAccount/Token
- 安全与最佳实践
针对“自动化脚本清理未使用客户端ID”的需求,需要明确“客户端ID”的上下文(如OAuth2客户端、数据库中的设备Token、容器中的Pod、或API密钥等),以下是通用的清理思路及脚本模板,适用于Web服务、OAuth2、物联网设备等场景。
核心清理逻辑
- 定义“未使用”:基于上次活跃时间(Last Active)、无API调用天数、或未关联有效用户。
- 查询候选ID:从系统日志、数据库或认证服务中提取。
- 执行清理:软删除(标记失效)或硬删除(移除记录)。
- 备份与验证:清理前导出列表,清理后检查服务健康。
基于数据库的客户端ID清理(通用SQL + Shell脚本)
场景:客户端ID存储在关系型数据库中(如MySQL/PostgreSQL),且有last_used_at或status字段。
识别90天未使用的客户端
-- 假设表名为 'oauth_clients' SELECT client_id, client_name, last_used_at FROM oauth_clients WHERE last_used_at < NOW() - INTERVAL 90 DAY OR last_used_at IS NULL; -- 从未使用过
自动化清理脚本(Bash)
#!/bin/bash
# clean_unused_clients.sh
DB_HOST="localhost"
DB_USER="admin"
DB_PASS="securepass"
DB_NAME="auth_db"
DRY_RUN=${1:-false} # 默认非干运行:传入"true"则只打印不删除
# 备份旧ID
BACKUP_FILE="/tmp/cleaned_client_ids_$(date +%Y%m%d_%H%M%S).csv"
echo "Backing up clients to $BACKUP_FILE"
mysql -h $DB_HOST -u $DB_USER -p$DB_PASS $DB_NAME -e \
"SELECT client_id, client_secret, created_at, last_used_at INTO OUTFILE '$BACKUP_FILE'
FIELDS TERMINATED BY ',' ENCLOSED BY '\"'
FROM oauth_clients
WHERE last_used_at < NOW() - INTERVAL 90 DAY" 2>/dev/null
if [ "$DRY_RUN" = true ]; then
echo "[DRY-RUN] Would delete the following clients:"
mysql -h $DB_HOST -u $DB_USER -p$DB_PASS $DB_NAME -e \
"SELECT client_id, last_used_at FROM oauth_clients WHERE last_used_at < NOW() - INTERVAL 90 DAY"
else
# 执行软删除(建议先软删除,保留恢复可能)
mysql -h $DB_HOST -u $DB_USER -p$DB_PASS $DB_NAME -e \
"UPDATE oauth_clients SET status='disabled', disabled_at=NOW()
WHERE last_used_at < NOW() - INTERVAL 90 DAY AND status='active'"
# 或者硬删除(谨慎使用)
# mysql -h $DB_HOST -u $DB_USER -p$DB_PASS $DB_NAME -e \
# "DELETE FROM oauth_clients WHERE last_used_at < NOW() - INTERVAL 90 DAY"
echo "[INFO] Cleanup completed. Disabled $(mysql -h $DB_HOST -u $DB_USER -p$DB_PASS $DB_NAME -N -e \
"SELECT COUNT(*) FROM oauth_clients WHERE status='disabled' AND disabled_at > NOW() - INTERVAL 1 MINUTE") clients."
fi
基于日志分析(ELK/Grafana + Python)
场景:客户端ID由微服务或API Gateway记录访问日志,无直接时间戳字段。
Python脚本:分析Nginx/AWS CloudWatch日志
#!/usr/bin/env python3
"""
clean_orphan_clients.py
从API访问日志中提取最近90天活跃的client_id,反向标记未出现的ID。
"""
import re
from datetime import datetime, timedelta
import redis
import requests
# 配置
LOG_FILE = '/var/log/nginx/api_access.log' # 或从Kafka/CloudWatch读取
REDIS_CLIENTS_KEY = 'active_clients:recent_90d'
API_DELETE_URL = 'https://api.example.com/admin/clients/{client_id}/disable'
# 1. 解析日志,收集活跃ID
active_ids = set()
with open(LOG_FILE, 'r') as f:
for line in f:
# 假设日志格式: [timestamp] client_id=xxx ...
match = re.search(r'client_id=([a-zA-Z0-9_-]+)', line)
if match:
active_ids.add(match.group(1))
# 2. 查询数据库/Redis中所有ID,找出差集
all_ids = redis_client.smembers('all_client_ids') # 假设预存在Redis
unused_ids = [cid for cid in all_ids if cid not in active_ids]
# 3. 执行清理
for client_id in unused_ids:
print(f"Disabling client_id: {client_id}")
resp = requests.post(API_DELETE_URL.format(client_id=client_id),
headers={'Authorization': 'Bearer admin-token'})
if resp.status_code != 200:
print(f"Failed to disable {client_id}")
Kubernetes环境清理过期的ServiceAccount/Token
场景:Kubernetes集群中存在大量未使用的ServiceAccount或绑定的Secret。
Bash脚本:清理有Tombstone的过期Token
#!/bin/bash
# clean_unused_sa_tokens.sh
K8S_NAMESPACE="default"
DAYS_OLD=90
# 获取所有ServiceAccount(排除系统组件)
echo "Listing ServiceAccounts older than $DAYS_OLD days..."
kubectl get sa --all-namespaces -o json | jq -r \
'.items[] | select(.metadata.creationTimestamp < (now - 86400*'$DAYS_OLD' | strftime("%Y-%m-%dT%H:%M:%SZ")))
| "\(.metadata.namespace) \(.metadata.name)"' | while read ns sa; do
# 检查该SA是否被Pod引用
POD_COUNT=$(kubectl get pods -n $ns -o json | jq -r \
"[.items[] | select(.spec.serviceAccountName==\"$sa\")] | length")
if [ "$POD_COUNT" -eq 0 ]; then
echo "Deleting unused SA: $ns/$sa"
# kubectl delete sa -n $ns $sa # 如需删除取消注释
fi
done
安全与最佳实践
- 永远使用干运行模式:在脚本中添加
--dry-run参数,第一次执行仅打印受影响ID。 - 软删除优先:将状态设为
disabled,保留7-30天观察期,确认无影响后硬删除。 - 速率限制:清理操作可能会触发告警或性能问题,建议批量处理(每次100条)。
- 通知与审计:清理前发送邮件/RocketChat通知,清理后记录日志到ELK。
示例:企业级清理脚本结构
# clean_clients_pipeline.yml
stages:
- name: audit
action: select_unused_ids # 查询并生成CSV
- name: validate
action: check_impact # 检查这些ID是否关联生产流量
- name: disable # 软禁用
action: update_status
params:
new_status: "pending_delete"
- name: final_delete # 30天后硬删除
action: delete_records
schedule: "0 0 1 * *" # 每月1号执行
根据你的具体技术栈:
- 若用Keycloak/Auth0:调用管理API批量删除(如
GET /clients?lastUsedBefore=...)。 - 若用JWT:只需撤销JWT的签发权限,无需物理删除客户端ID。
- 若用IoT设备:检查设备心跳,连续30天无心跳直接删除注册信息。
你可以根据实际系统选择对应片段,或进一步提供细节(如数据库类型、日志格式),以便我给出更精确的代码。