服务端安全加固:多维限流(按 IP/按门店)+ 敏感接口独立速率上限抵御 DDoS/刷接口; 登录暴力破解新增按来源 IP 锁定;反代后正确识别真实客户端 IP; 门店 custom_fields 轻量配置(录入默认值)透传保存。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y2Wdwo7SmgBJU37cBrkhPK
This commit is contained in:
@@ -1,12 +1,14 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/wangjia/jiu/backend/config"
|
||||
"github.com/wangjia/jiu/backend/internal/model"
|
||||
"github.com/wangjia/jiu/backend/testutil"
|
||||
)
|
||||
@@ -146,6 +148,50 @@ func TestAuthService_RefreshTokens_Invalid(t *testing.T) {
|
||||
assert.Nil(t, newPair)
|
||||
}
|
||||
|
||||
// TestLogin_AccountLockoutAfterMaxFailures 同一账号连续失败达阈值后锁定(回归)。
|
||||
func TestLogin_AccountLockoutAfterMaxFailures(t *testing.T) {
|
||||
db := testutil.SetupTestDB()
|
||||
shop := testutil.CreateTestShop(db, "LOCK_ACC")
|
||||
testutil.CreateTestUser(db, shop.ID, "admin", "password123", "admin")
|
||||
config.C.Session.MaxFailures = 3
|
||||
svc := NewAuthService(db)
|
||||
defer loginLim.reset("LOCK_ACC|admin")
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
_, _, err := svc.Login("LOCK_ACC", "admin", "wrong", DeviceInfo{Platform: "windows", IP: "10.0.0.1"})
|
||||
require.ErrorIs(t, err, ErrInvalidCredentials)
|
||||
}
|
||||
// 第 4 次即便密码正确也被锁定拒绝。
|
||||
_, _, err := svc.Login("LOCK_ACC", "admin", "password123", DeviceInfo{Platform: "windows", IP: "10.0.0.1"})
|
||||
assert.ErrorIs(t, err, ErrTooManyAttempts)
|
||||
}
|
||||
|
||||
// TestLogin_IPLockoutAcrossAccounts 单 IP 跨多个(不存在的)账号累计失败达 IP 阈值后锁该 IP。
|
||||
// 每次用不同用户名,账号维度永不触发锁定,只有 IP 维度会锁——验证 per-IP 防撞库 + 防内存灌爆。
|
||||
func TestLogin_IPLockoutAcrossAccounts(t *testing.T) {
|
||||
db := testutil.SetupTestDB()
|
||||
testutil.CreateTestShop(db, "LOCK_IP")
|
||||
config.C.Session.MaxFailures = 5
|
||||
config.C.Session.IPMaxFailures = 4
|
||||
const attackIP = "203.0.113.9"
|
||||
svc := NewAuthService(db)
|
||||
defer loginLim.reset("ip|" + attackIP)
|
||||
|
||||
// 4 次不同用户名(invalid_user),账号 key 各不相同永不锁;IP key 累计到 4 → 锁 IP。
|
||||
for i := 0; i < 4; i++ {
|
||||
uname := "ghost" + strconv.Itoa(i)
|
||||
_, _, err := svc.Login("LOCK_IP", uname, "whatever", DeviceInfo{Platform: "windows", IP: attackIP})
|
||||
require.ErrorIs(t, err, ErrInvalidCredentials)
|
||||
}
|
||||
// 同 IP 再来一发(仍是新用户名,账号维度无锁)→ 被 IP 锁拦下。
|
||||
_, _, err := svc.Login("LOCK_IP", "ghostX", "whatever", DeviceInfo{Platform: "windows", IP: attackIP})
|
||||
assert.ErrorIs(t, err, ErrTooManyAttempts)
|
||||
|
||||
// 另一 IP 不受影响。
|
||||
_, _, err = svc.Login("LOCK_IP", "ghostY", "whatever", DeviceInfo{Platform: "windows", IP: "198.51.100.7"})
|
||||
assert.ErrorIs(t, err, ErrInvalidCredentials)
|
||||
}
|
||||
|
||||
func TestHashPassword(t *testing.T) {
|
||||
hash, err := HashPassword("mypassword")
|
||||
require.NoError(t, err)
|
||||
|
||||
Reference in New Issue
Block a user