feat(backend): 会话安全加固 + 授权实时 phase + 首次使用自动试用
会话安全(jti 轮换 / 重用检测 / 改密吊销 / 禁用即时下线 / 清理 / 失败登录落库): - refresh token 轮换 jti + token-family 重用检测,旧 token 重放即吊销整条会话 - 改密码、停用用户即时吊销其全部活跃会话(revoked_by 审计) - 中间件 session JOIN user 校验,禁用/删除用户带 token 请求返回 401 USER_DISABLED - 新增 login_attempts 失败登录落库 + 会话保留期清理 goroutine 授权实时 phase + 心跳回带: - LicenseGuard 改为按当前 DB 实时计算 phase(30s 每店缓存),续费/过期/被改 ~30s 内对写操作生效,无需重登 - /auth/ping 回带授权概况(ShopInfoView,与 /license/info 同构),客户端一次心跳即刷新横幅/门禁 首次使用自动试用 + code-review 修复: - 门店首次登录/续期无有效授权时自动签发 30 天 trial(快路径无锁 Count,仅首用走 FOR UPDATE 事务) - ShopInfo 区分「确无授权」与瞬时 DB 错误,避免误降级 - trial 签发后改为在事务提交后再失效 phase 缓存(修复早于提交的竞态) - 存量无 sid token 续期纳入显式上限,legacy 会话不再游离于并发配额之外 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -67,10 +67,12 @@ CREATE TABLE IF NOT EXISTS `user_sessions` (
|
||||
`platform_class` VARCHAR(20) DEFAULT NULL COMMENT 'desktop|mobile|web',
|
||||
`ip` VARCHAR(64) DEFAULT NULL,
|
||||
`user_agent` VARCHAR(512) DEFAULT NULL,
|
||||
`refresh_jti` VARCHAR(64) DEFAULT NULL COMMENT '当前有效 refresh token 的 jti,用于轮换+重用检测',
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`last_seen_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`revoked_at` DATETIME DEFAULT NULL,
|
||||
`revoked_reason` VARCHAR(30) DEFAULT NULL COMMENT 'kicked|logout|admin|disabled',
|
||||
`revoked_reason` VARCHAR(30) DEFAULT NULL COMMENT 'kicked|logout|admin|disabled|reuse|pwd_reset',
|
||||
`revoked_by` BIGINT UNSIGNED DEFAULT NULL COMMENT '吊销操作人 user_id;系统/自助吊销为 NULL',
|
||||
`refresh_exp_at` DATETIME DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_session_sid` (`sid`),
|
||||
@@ -79,6 +81,23 @@ CREATE TABLE IF NOT EXISTS `user_sessions` (
|
||||
KEY `idx_session_revoked` (`revoked_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='登录会话';
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 登录尝试审计(当前只记失败尝试,用于风控/排查;由保留期清理任务定期删旧)
|
||||
-- ------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS `login_attempts` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`shop_code` VARCHAR(64) DEFAULT NULL,
|
||||
`username` VARCHAR(50) DEFAULT NULL,
|
||||
`ip` VARCHAR(64) DEFAULT NULL,
|
||||
`user_agent` VARCHAR(512) DEFAULT NULL,
|
||||
`success` TINYINT(1) NOT NULL DEFAULT 0,
|
||||
`reason` VARCHAR(40) DEFAULT NULL COMMENT 'invalid_shop|invalid_user|bad_password|locked|inactive|platform_not_allowed',
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_attempt_user` (`username`, `created_at`),
|
||||
KEY `idx_attempt_ip` (`ip`, `created_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='登录尝试审计';
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 许可证
|
||||
-- ------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user