Files
VibeCoding/WebRTCSignalServer/docs/schema.sql
tongtongstudio 6eb2c7321a feat(controlled): 实现设备激活与安全认证流程
- 添加API客户端、加密存储和provision/token激活逻辑
- WebSocket改用Bearer令牌认证,移除REGISTER请求
- 设备ID改为服务端下发,支持令牌刷新和强制下线处理
- 新增deviceSecret加密存储和accessToken自动刷新
- 更新设备ID获取方式为出厂SN,添加安全存储依赖
2026-08-01 15:13:12 +08:00

285 lines
13 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- =============================================================
-- WebRTCSignalServer 数据库建表脚本 (MySQL 8.0+)
--
-- 【请手动执行,服务端代码当前仍为内存实现,不会自动建表】
--
-- 执行方式:
-- mysql -u root -p < schema.sql
--
-- 字符集统一使用 utf8mb4 / utf8mb4_0900_ai_ci
-- 所有时间字段使用 DATETIME(3),由应用层写入 UTC 时间
-- =============================================================
CREATE DATABASE IF NOT EXISTS `webrtc_signal`
DEFAULT CHARACTER SET utf8mb4
DEFAULT COLLATE utf8mb4_0900_ai_ci;
USE `webrtc_signal`;
-- =============================================================
-- 1. 主控端用户账号
-- =============================================================
CREATE TABLE IF NOT EXISTS `app_user` (
`user_id` VARCHAR(32) NOT NULL COMMENT '用户ID格式 usr_<20位Base62>',
`username` VARCHAR(32) NOT NULL COMMENT '登录名',
`password_hash` VARCHAR(100) NOT NULL COMMENT 'BCrypt 哈希cost=12',
`status` VARCHAR(16) NOT NULL DEFAULT 'ACTIVE'
COMMENT '账号状态ACTIVE/SUSPENDED/BANNED',
`status_until` DATETIME(3) NULL COMMENT '临时封禁到期时间NULL 表示永久或未封禁',
`status_reason` VARCHAR(255) NULL COMMENT '封禁原因',
`token_version` BIGINT NOT NULL DEFAULT 1
COMMENT '凭据版本号,递增后所有已签发令牌立即失效',
`is_admin` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否管理员',
-- 双因子认证
`totp_secret` VARCHAR(64) NULL COMMENT 'TOTP 密钥Base32NULL 表示未启用',
`totp_enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'TOTP 是否已完成绑定',
-- 防爆破
`failed_attempts` INT NOT NULL DEFAULT 0 COMMENT '连续登录失败次数',
`locked_until` DATETIME(3) NULL COMMENT '锁定截止时间',
`last_login_at` DATETIME(3) NULL,
`last_login_ip` VARCHAR(64) NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3)
ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (`user_id`),
UNIQUE KEY `uk_username` (`username`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB COMMENT='主控端用户账号';
-- =============================================================
-- 2. 密码历史(防止重复使用近期密码)
-- =============================================================
CREATE TABLE IF NOT EXISTS `password_history` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`user_id` VARCHAR(32) NOT NULL,
`password_hash` VARCHAR(100) NOT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
KEY `idx_user_created` (`user_id`, `created_at` DESC),
CONSTRAINT `fk_pwdhist_user` FOREIGN KEY (`user_id`)
REFERENCES `app_user` (`user_id`) ON DELETE CASCADE
) ENGINE=InnoDB COMMENT='密码历史,建议保留最近 5 条';
-- =============================================================
-- 3. 登录会话(刷新令牌上下文)
-- =============================================================
CREATE TABLE IF NOT EXISTS `login_session` (
`session_id` VARCHAR(32) NOT NULL COMMENT '会话ID格式 ses_<20位Base62>',
`principal_id` VARCHAR(32) NOT NULL COMMENT '主体IDuser_id',
`principal_type` VARCHAR(16) NOT NULL DEFAULT 'USER' COMMENT 'USER/DEVICE',
`refresh_token_hash` VARCHAR(64) NOT NULL COMMENT '当前刷新令牌的 SHA-256轮转后更新',
`refresh_expires_at` DATETIME(3) NOT NULL,
`ip` VARCHAR(64) NULL,
`user_agent` VARCHAR(256) NULL,
`revoked` TINYINT(1) NOT NULL DEFAULT 0,
`revoked_reason` VARCHAR(255) NULL,
`last_seen_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`session_id`),
KEY `idx_principal` (`principal_id`, `revoked`),
KEY `idx_expires` (`refresh_expires_at`)
) ENGINE=InnoDB COMMENT='登录会话,支持踢线与刷新令牌轮转';
-- =============================================================
-- 4. SN 白名单(出厂/部署时批量导入)
-- =============================================================
CREATE TABLE IF NOT EXISTS `allowed_device` (
`sn` VARCHAR(64) NOT NULL COMMENT '设备序列号',
`batch` VARCHAR(64) NULL COMMENT '批次标识',
`note` VARCHAR(255) NULL,
`imported_by` VARCHAR(64) NULL,
`imported_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`sn`),
KEY `idx_batch` (`batch`)
) ENGINE=InnoDB COMMENT='允许激活的 SN 白名单';
-- =============================================================
-- 5. 被控端设备
-- SN 仅作内部主键,对外一律使用高熵不可枚举的 device_uid
-- =============================================================
CREATE TABLE IF NOT EXISTS `device` (
`device_uid` VARCHAR(32) NOT NULL COMMENT '对外设备ID格式 dev_<22位Base62>',
`sn` VARCHAR(64) NOT NULL COMMENT '设备序列号(内部使用,勿对外暴露)',
`secret_hash` VARCHAR(64) NOT NULL COMMENT 'deviceSecret 的 SHA-256',
`model` VARCHAR(64) NULL,
`status` VARCHAR(16) NOT NULL DEFAULT 'ACTIVE'
COMMENT 'ACTIVE/SUSPENDED/BANNED',
`status_until` DATETIME(3) NULL,
`status_reason` VARCHAR(255) NULL,
`token_version` BIGINT NOT NULL DEFAULT 1,
-- 防骚扰配置P1 阶段使用)
`strict_mode` TINYINT(1) NOT NULL DEFAULT 1
COMMENT '严格模式:仅允许已绑定账号连接',
`quiet_start` TIME NULL COMMENT '勿扰时段开始',
`quiet_end` TIME NULL COMMENT '勿扰时段结束',
`provisioned_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`last_online_at` DATETIME(3) NULL,
PRIMARY KEY (`device_uid`),
UNIQUE KEY `uk_sn` (`sn`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB COMMENT='被控端设备身份';
-- =============================================================
-- 6. 设备激活 nonce防重放
-- 建议配合定时任务清理过期记录,或改用 Redis
-- =============================================================
CREATE TABLE IF NOT EXISTS `provision_nonce` (
`nonce` VARCHAR(64) NOT NULL,
`sn` VARCHAR(64) NULL,
`expires_at` DATETIME(3) NOT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`nonce`),
KEY `idx_expires` (`expires_at`)
) ENGINE=InnoDB COMMENT='激活请求 nonce用于防重放';
-- =============================================================
-- 7. 设备绑定关系P1 阶段:主控端唯一寻址凭证)
-- =============================================================
CREATE TABLE IF NOT EXISTS `device_binding` (
`binding_id` VARCHAR(36) NOT NULL COMMENT 'UUID',
`device_uid` VARCHAR(32) NOT NULL,
`user_id` VARCHAR(32) NOT NULL,
`role` VARCHAR(16) NOT NULL DEFAULT 'OPERATOR'
COMMENT 'OWNER/OPERATOR/VIEWER',
`alias` VARCHAR(64) NULL COMMENT '主控端自定义备注名',
`status` VARCHAR(16) NOT NULL DEFAULT 'ACTIVE' COMMENT 'ACTIVE/REVOKED',
`bound_by` VARCHAR(32) NULL,
`bound_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`expire_at` DATETIME(3) NULL COMMENT '绑定过期时间NULL 表示长期有效',
PRIMARY KEY (`binding_id`),
UNIQUE KEY `uk_device_user` (`device_uid`, `user_id`),
KEY `idx_user_status` (`user_id`, `status`),
KEY `idx_device_status` (`device_uid`, `status`)
) ENGINE=InnoDB COMMENT='设备与账号绑定关系';
-- =============================================================
-- 8. 配对码P1 阶段:建立绑定)
-- =============================================================
CREATE TABLE IF NOT EXISTS `pairing_code` (
`code_hash` VARCHAR(64) NOT NULL COMMENT '配对码的 SHA-256',
`device_uid` VARCHAR(32) NOT NULL,
`attempts` INT NOT NULL DEFAULT 0 COMMENT '错误尝试次数,超限即失效',
`used` TINYINT(1) NOT NULL DEFAULT 0,
`expires_at` DATETIME(3) NOT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`code_hash`),
KEY `idx_device` (`device_uid`),
KEY `idx_expires` (`expires_at`)
) ENGINE=InnoDB COMMENT='一次性配对码';
-- =============================================================
-- 9. 被控端黑名单P1 阶段:拉黑骚扰账号)
-- =============================================================
CREATE TABLE IF NOT EXISTS `device_blacklist` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`device_uid` VARCHAR(32) NOT NULL,
`blocked_user_id` VARCHAR(32) NOT NULL,
`reason` VARCHAR(255) NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
UNIQUE KEY `uk_device_user` (`device_uid`, `blocked_user_id`),
KEY `idx_device` (`device_uid`)
) ENGINE=InnoDB COMMENT='被控端黑名单';
-- =============================================================
-- 10. 连接会话记录WebRTC 通话审计)
-- =============================================================
CREATE TABLE IF NOT EXISTS `connection_session` (
`session_id` VARCHAR(36) NOT NULL,
`binding_id` VARCHAR(36) NULL,
`device_uid` VARCHAR(32) NOT NULL,
`user_id` VARCHAR(32) NOT NULL,
`auth_type` VARCHAR(16) NULL COMMENT 'NONE/CODE/PASSWORD',
`started_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`ended_at` DATETIME(3) NULL,
`end_reason` VARCHAR(64) NULL,
PRIMARY KEY (`session_id`),
KEY `idx_device_started` (`device_uid`, `started_at` DESC),
KEY `idx_user_started` (`user_id`, `started_at` DESC)
) ENGINE=InnoDB COMMENT='远程控制会话记录';
-- =============================================================
-- 11. 审计日志(只追加,不修改)
-- =============================================================
CREATE TABLE IF NOT EXISTS `audit_log` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`actor_type` VARCHAR(16) NOT NULL COMMENT 'USER/DEVICE/ADMIN/SYSTEM',
`actor_id` VARCHAR(64) NULL,
`action` VARCHAR(64) NOT NULL
COMMENT 'LOGIN/LOGIN_FAILED/LOGOUT/REGISTER/PROVISION/BAN/UNBAN/KICK/BIND/UNBIND/BLACKLIST...',
`target_type` VARCHAR(16) NULL,
`target_id` VARCHAR(64) NULL,
`result` VARCHAR(16) NOT NULL DEFAULT 'SUCCESS' COMMENT 'SUCCESS/FAILURE',
`ip` VARCHAR(64) NULL,
`user_agent` VARCHAR(256) NULL,
`detail` VARCHAR(512) NULL COMMENT '注意:敏感信息需脱敏后再写入',
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
KEY `idx_actor` (`actor_id`, `created_at` DESC),
KEY `idx_action` (`action`, `created_at` DESC),
KEY `idx_created` (`created_at` DESC)
) ENGINE=InnoDB COMMENT='安全审计日志';
-- =============================================================
-- 12. 骚扰举报P2 阶段风控)
-- =============================================================
CREATE TABLE IF NOT EXISTS `abuse_report` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`device_uid` VARCHAR(32) NOT NULL COMMENT '举报方(被控端)',
`reported_user_id` VARCHAR(32) NOT NULL COMMENT '被举报账号',
`reason` VARCHAR(255) NULL,
`handled` TINYINT(1) NOT NULL DEFAULT 0,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
KEY `idx_reported` (`reported_user_id`, `created_at` DESC),
KEY `idx_handled` (`handled`)
) ENGINE=InnoDB COMMENT='骚扰举报记录';
-- =============================================================
-- 清理建议(可配置为 MySQL Event 或应用层定时任务)
-- =============================================================
-- DELETE FROM provision_nonce WHERE expires_at < NOW();
-- DELETE FROM pairing_code WHERE expires_at < NOW();
-- DELETE FROM login_session WHERE refresh_expires_at < NOW() OR revoked = 1;
-- DELETE FROM audit_log WHERE created_at < DATE_SUB(NOW(), INTERVAL 180 DAY);
-- =============================================================
-- 初始管理员账号
-- =============================================================
-- 不在此处插入明文密码。请通过环境变量启动服务自动创建:
-- BOOTSTRAP_ADMIN_USERNAME=admin
-- BOOTSTRAP_ADMIN_PASSWORD=<强密码>
-- 或注册后手动提升权限:
-- UPDATE app_user SET is_admin = 1 WHERE username = 'admin';