feat(dcauth): 新增后台手机号与设备认证登录
登录链路改为账号密码 + 短信验证码 + DCAuth 设备签名,并新增后台手机号绑定、 登录日志、设备绑定与重置接口。
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
-- 102_admin_phone_device_auth.sql —— 后台账号手机号、短信验证码与设备认证。
|
||||
--
|
||||
-- 登录链路:账号/密码 + 短信验证码 + DCAuth 设备签名。
|
||||
-- 旧账号未绑定手机号时仍可使用原密码登录;绑定手机号后强制走新链路。
|
||||
|
||||
ALTER TABLE admin_users
|
||||
ADD COLUMN IF NOT EXISTS phone TEXT NOT NULL DEFAULT '';
|
||||
ALTER TABLE admin_users
|
||||
ADD COLUMN IF NOT EXISTS phone_bound_at TIMESTAMPTZ;
|
||||
ALTER TABLE admin_users
|
||||
ADD COLUMN IF NOT EXISTS phone_bound_by BIGINT;
|
||||
ALTER TABLE admin_users
|
||||
ADD COLUMN IF NOT EXISTS test_account BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM pg_constraint
|
||||
WHERE conname = 'fk_admin_users_phone_bound_by'
|
||||
) THEN
|
||||
ALTER TABLE admin_users
|
||||
ADD CONSTRAINT fk_admin_users_phone_bound_by
|
||||
FOREIGN KEY (phone_bound_by) REFERENCES admin_users(id) ON DELETE SET NULL;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_admin_users_phone
|
||||
ON admin_users(phone)
|
||||
WHERE phone <> '';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS admin_sms_codes (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
user_id BIGINT NOT NULL REFERENCES admin_users(id) ON DELETE CASCADE,
|
||||
phone TEXT NOT NULL,
|
||||
purpose TEXT NOT NULL DEFAULT 'login',
|
||||
code_hash TEXT NOT NULL,
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
consumed_at TIMESTAMPTZ,
|
||||
failed_attempt_count INTEGER NOT NULL DEFAULT 0,
|
||||
request_ip TEXT NOT NULL DEFAULT '',
|
||||
created_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_admin_sms_codes_user_created
|
||||
ON admin_sms_codes(user_id, purpose, created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_admin_sms_codes_phone_created
|
||||
ON admin_sms_codes(phone, created_at DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS admin_user_devices (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
user_id BIGINT NOT NULL REFERENCES admin_users(id) ON DELETE CASCADE,
|
||||
device_id TEXT NOT NULL,
|
||||
public_key TEXT NOT NULL,
|
||||
machine_code_hash TEXT NOT NULL,
|
||||
machine_code_mask TEXT NOT NULL DEFAULT '',
|
||||
client_version TEXT NOT NULL DEFAULT '',
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
bound_at TIMESTAMPTZ NOT NULL,
|
||||
last_used_at TIMESTAMPTZ,
|
||||
revoked_at TIMESTAMPTZ,
|
||||
revoked_by BIGINT REFERENCES admin_users(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_admin_user_devices_active_user
|
||||
ON admin_user_devices(user_id)
|
||||
WHERE status = 'active';
|
||||
CREATE INDEX IF NOT EXISTS idx_admin_user_devices_device_id
|
||||
ON admin_user_devices(device_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS admin_device_challenges (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
challenge_id TEXT NOT NULL UNIQUE,
|
||||
app TEXT NOT NULL,
|
||||
purpose TEXT NOT NULL DEFAULT 'login',
|
||||
challenge TEXT NOT NULL,
|
||||
poll_token_hash TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
user_id BIGINT REFERENCES admin_users(id) ON DELETE SET NULL,
|
||||
device_id TEXT NOT NULL DEFAULT '',
|
||||
public_key TEXT NOT NULL DEFAULT '',
|
||||
machine_code_hash TEXT NOT NULL DEFAULT '',
|
||||
machine_code_mask TEXT NOT NULL DEFAULT '',
|
||||
client_version TEXT NOT NULL DEFAULT '',
|
||||
device_ticket_hash TEXT NOT NULL DEFAULT '',
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
authorized_at TIMESTAMPTZ,
|
||||
ticket_issued_at TIMESTAMPTZ,
|
||||
consumed_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_admin_device_challenges_expires
|
||||
ON admin_device_challenges(expires_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_admin_device_challenges_user
|
||||
ON admin_device_challenges(user_id, created_at DESC);
|
||||
|
||||
ALTER TABLE admin_login_logs
|
||||
ADD COLUMN IF NOT EXISTS login_method TEXT NOT NULL DEFAULT 'password';
|
||||
ALTER TABLE admin_login_logs
|
||||
ADD COLUMN IF NOT EXISTS machine_code_mask TEXT NOT NULL DEFAULT '';
|
||||
|
||||
ALTER TABLE admin_sessions
|
||||
ADD COLUMN IF NOT EXISTS device_id TEXT NOT NULL DEFAULT '';
|
||||
|
||||
COMMENT ON COLUMN admin_users.phone IS '后台账号绑定手机号,用于登录短信验证码';
|
||||
COMMENT ON COLUMN admin_users.test_account IS '技术测试账号:允许仅账号密码登录,跳过短信和 DCAuth 设备验证';
|
||||
COMMENT ON TABLE admin_sms_codes IS '后台登录短信验证码(哈希存储、一次性消费)';
|
||||
COMMENT ON TABLE admin_user_devices IS '后台账号绑定的 DCAuth 设备(公钥 + 机器码哈希)';
|
||||
COMMENT ON TABLE admin_device_challenges IS 'DCAuth 设备认证 challenge 与一次性 ticket';
|
||||
COMMENT ON COLUMN admin_login_logs.login_method IS 'password / sms_device';
|
||||
COMMENT ON COLUMN admin_login_logs.machine_code_mask IS '登录设备机器码脱敏值';
|
||||
COMMENT ON COLUMN admin_sessions.device_id IS '登录会话绑定的设备 ID';
|
||||
Reference in New Issue
Block a user