-- ============================================================= -- 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 密钥(Base32),NULL 表示未启用', `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 '主体ID(user_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';