Scenario 3: 帳號鎖定處理
User Story: US-001: 用戶登入
Given: 系統初始狀態
已鎖定的用戶
{
"userId": "user-001",
"email": "staff@florist.com",
"passwordHash": "$2b$12$...",
"name": "王小明",
"roles": ["ROLE_STAFF"],
"tenantId": "tenant-abc",
"loginFailedCount": 5,
"lockedUntil": "2025-10-31T10:45:00Z" // 鎖定至此時間
}
當前時間: 2025-10-31T10:30:00Z(在鎖定期間內)
When: 執行操作
API 請求(使用正確密碼)
POST /api/v1/auth/login HTTP/1.1
Content-Type: application/json
{
"email": "staff@florist.com",
"password": "Password123!", // 正確密碼
"rememberMe": false
}
Then: 預期結果
系統響應 (423 Locked)
{
"error": "ACCOUNT_LOCKED",
"message": "帳號已被鎖定,請 15 分鐘後再試",
"lockedUntil": "2025-10-31T10:45:00Z",
"timestamp": "2025-10-31T10:30:00Z"
}
數據庫變更
- 無變更(鎖定期間不重置計數器,防止暴力破解)
審計日誌
{
"id": "log-001",
"tenantId": "tenant-abc",
"userId": "user-001",
"action": "USER_LOGIN",
"resourceType": "User",
"resourceId": "user-001",
"status": "FAILED",
"details": {
"reason": "ACCOUNT_LOCKED",
"lockedUntil": "2025-10-31T10:45:00Z"
},
"ipAddress": "192.168.1.100",
"timestamp": "2025-10-31T10:30:00Z"
}
前端行為
- Toast 通知顯示「帳號已被鎖定,請 15 分鐘後再試」(紅色,5 秒)
- 顯示倒數計時器(顯示剩餘鎖定時間)
- 「登入」按鈕禁用
- 停留在登入頁面
邊界條件: 鎖定期過後自動解鎖
第二次請求(鎖定期過後)
當前時間: 2025-10-31T10:46:00Z(鎖定期已過)
API 請求(使用正確密碼)
POST /api/v1/auth/login HTTP/1.1
Content-Type: application/json
{
"email": "staff@florist.com",
"password": "Password123!",
"rememberMe": false
}
系統響應 (200 OK)
{
"accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "user-001",
"email": "staff@florist.com",
"name": "王小明",
"roles": ["ROLE_STAFF"],
"tenantId": "tenant-abc",
"tenantName": "花店 ABC"
},
"expiresIn": 900
}
users 表更新
{
"userId": "user-001",
"loginFailedCount": 0, // 重置為 0
"lockedUntil": null, // 解鎖
"lastLoginAt": "2025-10-31T10:46:00Z"
}
前端行為
- Toast 通知顯示「登入成功」(綠色,2 秒)
- 重定向至首頁 (
/)
副作用: 發送帳號鎖定通知 (Phase 2)
當帳號首次被鎖定時,發送 Email 通知:
Email 內容
主旨: 【安全警告】您的帳號已被鎖定
親愛的王小明,您好!
我們偵測到您的帳號 staff@florist.com 因連續登入失敗 5 次而被鎖定。
鎖定時間: 2025-10-31 10:30
解鎖時間: 2025-10-31 10:45 (15 分鐘後)
如果這不是您本人的操作,請立即聯繫系統管理員。
花店 ABC 系統
最後更新: 2025-10-31