本系统实现了一套完整的用户认证和账号共享检测机制,基于 Vue 3 + Supabase Auth + Python 后端,旨在防止多人共享同一账号。
- 使用 Supabase Auth 进行身份验证
- 用户名 + 密码登录方式
- 账号由管理员手动创建(格式:
username@internal.local) - 支持会话管理和自动恢复
- 规则:每个账号同时只允许 1 个活跃会话
- 活跃窗口:15 分钟(基于
last_seen_at字段) - 行为:新登录会自动踢掉旧会话
系统收集以下设备特征生成指纹:
- Canvas 指纹(权重 30%)
- Audio 指纹(权重 20%)
- 屏幕分辨率和像素比(权重 20%)
- 操作系统平台(权重 10%)
- 浏览器信息(权重 10%)
- 时区偏移(权重 5%)
- 硬件并发数(权重 5%)
- 使用加权算法计算新旧设备指纹的相似度(0-1)
- 阈值:相似度 < 0.5 判定为不同设备
- 存储格式:
fingerprint_raw:JSONB 格式,用于相似度计算fingerprint_hash:SHA-256 哈希,用于快速比对
- 初始分数:0 分
- 触发条件:检测到不同设备登录时 +15 分
- 账号状态:
active(正常):< 40 分limited(受限):40-70 分banned(封禁):≥ 70 分
- 频率:每 60 秒
- 功能:
- 更新
last_seen_at时间戳 - 检查账号状态变化
- 自动登出被封禁账号
- 更新
登录 → 创建会话 → 启动心跳 → 持续更新 → 登出/超时
- id: UUID (主键)
- user_id: UUID (外键 → auth.users)
- username: VARCHAR(50) (唯一)
- account_status: VARCHAR(20) (active/limited/banned)
- risk_score: INTEGER (风险分数)
- last_login_at: TIMESTAMPTZ
- created_at, updated_at, created_by, updated_by- id: UUID (主键)
- user_id: UUID (外键 → auth.users)
- fingerprint_raw: JSONB (设备指纹原始数据)
- fingerprint_hash: VARCHAR(64) (SHA-256 哈希)
- ip_address: INET
- user_agent: TEXT
- is_active: BOOLEAN
- last_seen_at: TIMESTAMPTZ (心跳更新)
- similarity_score: NUMERIC(5,2)
- kicked_reason: TEXT- id: UUID (主键)
- user_id: UUID (外键 → auth.users)
- detected_at: TIMESTAMPTZ
- event_type: VARCHAR(50) (事件类型)
- details: JSONB (详细信息)
- risk_score_change: INTEGER
- state_change: VARCHAR(20)功能:用户登录
请求体:
{
"username": "string",
"password": "string",
"fingerprint_raw": {
"canvas_hash": "string",
"audio_hash": "string",
"screen_width": 1920,
...
},
"fingerprint_hash": "sha256_hash"
}响应:
{
"success": true,
"data": {
"user": {
"id": "uuid",
"username": "string",
"account_status": "active",
"risk_score": 0
},
"session": {
"id": "uuid",
"created_at": "timestamp"
}
}
}处理流程:
- Supabase Auth 验证用户名密码
- 检查账号状态(banned 直接拒绝)
- 查询活跃会话(15分钟内)
- 计算设备指纹相似度
- 如果相似度 < 0.5:
- 增加风险分数 +15
- 记录异常日志
- 踢掉旧会话
- 创建新会话
- 更新用户资料(last_login_at, risk_score, account_status)
功能:用户登出
请求体:
{
"session_id": "uuid"
}处理流程:
- 设置
is_active = false - 更新
last_seen_at
功能:会话心跳
请求体:
{
"session_id": "uuid"
}响应:
{
"success": true,
"data": {
"force_logout": false,
"account_status": "active"
}
}处理流程:
- 更新
last_seen_at - 检查账号状态
- 如果
banned,返回force_logout: true
src/
├── composables/
│ └── useAuth.js # 认证状态管理
├── utils/
│ └── fingerprint.js # 设备指纹收集
├── views/
│ ├── Login.vue # 登录页面
│ └── Home.vue # 首页(需要认证)
├── components/
│ └── Header.vue # 头部(显示用户名和登出按钮)
└── router/
└── index.js # 路由配置(路由守卫)
提供全局认证状态和方法:
- 状态:
user,session,loading,error,isAuthenticated - 方法:
login(),logout(),restoreSession(),checkAuth() - 心跳:自动启动/停止 60 秒心跳定时器
router.beforeEach((to, from, next) => {
const { checkAuth } = useAuth()
const isAuthenticated = checkAuth()
if (to.meta.requiresAuth && !isAuthenticated) {
next({ name: 'Login' }) // 未登录跳转到登录页
} else if (to.name === 'Login' && isAuthenticated) {
next({ name: 'Home' }) // 已登录跳转到首页
} else {
next()
}
})- 并发会话限制
- 设备指纹识别
- 风险分数累积
- 三级账号状态(active → limited → banned)
- 15 分钟活跃窗口
- 自动心跳检测
- 服务端时间戳(防止客户端时间篡改)
- 强制登出机制
- 使用 Supabase Service Role Key(服务端)
- CORS 配置
- 密码通过 Supabase Auth 加密存储
- 设备指纹双重存储(raw + hash)
系统会记录以下异常事件到 account_anomaly_logs 表:
| 事件类型 | 触发条件 | 风险分数变化 |
|---|---|---|
concurrent_login_different_device |
相似度 < 0.5 | +15 |
- 用户首次登录
- 创建会话,风险分数 0
- 账号状态:active
- 用户在同一设备再次登录
- 计算相似度 > 0.5
- 踢掉旧会话,创建新会话
- 风险分数不变
- 用户 A 在设备 A 登录
- 用户 B 在设备 B 使用相同账号登录
- 计算相似度 < 0.5
- 风险分数 +15
- 记录异常日志
- 如果累计 ≥ 70 分,账号被封禁
- 风险分数达到 70 分
- 账号状态变为
banned - 下次心跳时强制登出
- 无法再次登录
SUPABASE_URL=your_supabase_url
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key
ALLOWED_ORIGIN=* # 生产环境应设置为具体域名-
Supabase 配置:
- 确保
auth.users表已创建 - 配置 RLS 策略
- 使用 Service Role Key(服务端)
- 确保
-
Vercel 部署:
- Python API 函数位于
/api目录 - 配置环境变量
- 安装依赖:
supabase-py
- Python API 函数位于
-
前端构建:
- 安装依赖:
npm install - 构建:
npm run build
- 安装依赖:
-
定期清理:
- 清理超过 15 分钟的非活跃会话
- 归档旧的异常日志
-
监控指标:
- 每日登录次数
- 异常检测触发次数
- 账号封禁数量
-
风险分数衰减(可选):
- 考虑实现风险分数随时间衰减机制
- 例如:每 30 天 -5 分(最低 0 分)
A: 平衡用户体验和安全性。太短会频繁踢人,太长无法及时检测共享。
A: 会。浏览器更新、屏幕分辨率改变等都可能影响指纹。因此使用相似度而非完全匹配。
A: 需要管理员手动修改 user_profiles 表的 account_status 和 risk_score。
A: 可以。修改 api/auth/login.py 中的阈值逻辑即可。
- 初始版本
- 实现基础认证功能
- 实现账号共享检测
- 实现风险分数系统
- 实现会话心跳机制