fix(backend): 后台注册限流配置优先并保留注入兜底
This commit is contained in:
1 parent
903e7db085
commit
35d92c0148
4 files changed
+23
-8
No files matched your search
+4
-1
@@ -125,7 +125,10 @@ app.register.max-per-ip-per-day
|
|||||||
app.register.max-per-device-per-day
|
app.register.max-per-device-per-day
|
||||||
```
|
```
|
||||||
|
|
||||||
键缺失或值不是整数时回退默认 5 / 3;值小于等于 0 表示对应维度不限制。
|
运行时优先读取 `sys_config`。键缺失或值不是整数时,回退 `application.yml`
|
||||||
|
中的 `fruitshorts.auth.register-max-per-ip-per-day` /
|
||||||
|
`fruitshorts.auth.register-max-per-device-per-day`,缺省为 5 / 3;
|
||||||
|
值小于等于 0 表示对应维度不限制。
|
||||||
开发种子配置为 0 / 0,生产建议恢复为 5 / 3。
|
开发种子配置为 0 / 0,生产建议恢复为 5 / 3。
|
||||||
|
|
||||||
**残留风险**:IP 取自 `X-Forwarded-For`、设备号由客户端上报,都能伪造,只能挡脚本批量;
|
**残留风险**:IP 取自 `X-Forwarded-For`、设备号由客户端上报,都能伪造,只能挡脚本批量;
|
||||||
|
|||||||
@@ -43,6 +43,10 @@ springdoc:
|
|||||||
path: /v3/api-docs
|
path: /v3/api-docs
|
||||||
|
|
||||||
fruitshorts:
|
fruitshorts:
|
||||||
|
auth:
|
||||||
|
# 仅作 sys_config 缺失/非法时的兜底;运行时优先读取后台「系统配置」
|
||||||
|
register-max-per-ip-per-day: ${REGISTER_MAX_PER_IP_PER_DAY:5}
|
||||||
|
register-max-per-device-per-day: ${REGISTER_MAX_PER_DEVICE_PER_DAY:3}
|
||||||
interaction:
|
interaction:
|
||||||
# MACHINE_FIRST:机审通过即发布,命中 REVIEW 级敏感词才转人工
|
# MACHINE_FIRST:机审通过即发布,命中 REVIEW 级敏感词才转人工
|
||||||
# PRE_REVIEW :全部先审后发(生产建议)
|
# PRE_REVIEW :全部先审后发(生产建议)
|
||||||
|
|||||||
+4
-3
@@ -26,6 +26,7 @@ import com.fruitshorts.user.vo.TokenRespVO;
|
|||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@@ -193,9 +194,9 @@ class AppAuthServiceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void should_fallback_to_default_limit_when_config_is_not_numeric() {
|
void should_fallback_to_default_limit_when_config_is_not_numeric() {
|
||||||
setRegisterLimits(5, 3);
|
ReflectionTestUtils.setField(authService, "maxRegisterPerIpPerDay", 2);
|
||||||
given(configService.get("app.register.max-per-ip-per-day", "5")).willReturn("invalid");
|
given(configService.get("app.register.max-per-ip-per-day", "2")).willReturn("invalid");
|
||||||
given(userMapper.selectCount(any())).willReturn(5L);
|
given(userMapper.selectCount(any())).willReturn(2L);
|
||||||
|
|
||||||
assertThatThrownBy(() -> authService.register(emailRegister("abcd1234", null), "203.0.113.8"))
|
assertThatThrownBy(() -> authService.register(emailRegister("abcd1234", null), "203.0.113.8"))
|
||||||
.isInstanceOf(ServiceException.class)
|
.isInstanceOf(ServiceException.class)
|
||||||
|
|||||||
+11
-4
@@ -26,6 +26,7 @@ import com.fruitshorts.user.vo.LogoutReqVO;
|
|||||||
import com.fruitshorts.user.vo.RegisterReqVO;
|
import com.fruitshorts.user.vo.RegisterReqVO;
|
||||||
import com.fruitshorts.user.vo.TokenRespVO;
|
import com.fruitshorts.user.vo.TokenRespVO;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.dao.DuplicateKeyException;
|
import org.springframework.dao.DuplicateKeyException;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -61,6 +62,14 @@ public class AppAuthService {
|
|||||||
private final PasswordEncoder passwordEncoder;
|
private final PasswordEncoder passwordEncoder;
|
||||||
private final ConfigService configService;
|
private final ConfigService configService;
|
||||||
|
|
||||||
|
/** 后台 sys_config 缺失或非法时使用;小于等于 0 表示不限制。 */
|
||||||
|
@Value("${fruitshorts.auth.register-max-per-ip-per-day:5}")
|
||||||
|
private int maxRegisterPerIpPerDay = 5;
|
||||||
|
|
||||||
|
/** 后台 sys_config 缺失或非法时使用;小于等于 0 表示不限制。 */
|
||||||
|
@Value("${fruitshorts.auth.register-max-per-device-per-day:3}")
|
||||||
|
private int maxRegisterPerDevicePerDay = 3;
|
||||||
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public TokenRespVO register(RegisterReqVO req, String clientIp) {
|
public TokenRespVO register(RegisterReqVO req, String clientIp) {
|
||||||
String targetType = AppIdentifier.normalizeType(req.getTargetType());
|
String targetType = AppIdentifier.normalizeType(req.getTargetType());
|
||||||
@@ -305,8 +314,6 @@ public class AppAuthService {
|
|||||||
|
|
||||||
private static final String REGISTER_IP_LIMIT_KEY = "app.register.max-per-ip-per-day";
|
private static final String REGISTER_IP_LIMIT_KEY = "app.register.max-per-ip-per-day";
|
||||||
private static final String REGISTER_DEVICE_LIMIT_KEY = "app.register.max-per-device-per-day";
|
private static final String REGISTER_DEVICE_LIMIT_KEY = "app.register.max-per-device-per-day";
|
||||||
private static final int DEFAULT_REGISTER_IP_LIMIT = 5;
|
|
||||||
private static final int DEFAULT_REGISTER_DEVICE_LIMIT = 3;
|
|
||||||
|
|
||||||
private static final int PASSWORD_MIN_LENGTH = 8;
|
private static final int PASSWORD_MIN_LENGTH = 8;
|
||||||
private static final int PASSWORD_MAX_LENGTH = 64;
|
private static final int PASSWORD_MAX_LENGTH = 64;
|
||||||
@@ -339,8 +346,8 @@ public class AppAuthService {
|
|||||||
*/
|
*/
|
||||||
private void guardRegisterRisk(String clientIp, String deviceId) {
|
private void guardRegisterRisk(String clientIp, String deviceId) {
|
||||||
LocalDateTime since = LocalDateTime.now().minusDays(1);
|
LocalDateTime since = LocalDateTime.now().minusDays(1);
|
||||||
int ipLimit = registerLimit(REGISTER_IP_LIMIT_KEY, DEFAULT_REGISTER_IP_LIMIT);
|
int ipLimit = registerLimit(REGISTER_IP_LIMIT_KEY, maxRegisterPerIpPerDay);
|
||||||
int deviceLimit = registerLimit(REGISTER_DEVICE_LIMIT_KEY, DEFAULT_REGISTER_DEVICE_LIMIT);
|
int deviceLimit = registerLimit(REGISTER_DEVICE_LIMIT_KEY, maxRegisterPerDevicePerDay);
|
||||||
String ip = AppIdentifier.nullSafe(clientIp);
|
String ip = AppIdentifier.nullSafe(clientIp);
|
||||||
if (ipLimit > 0 && StringUtils.hasText(ip)) {
|
if (ipLimit > 0 && StringUtils.hasText(ip)) {
|
||||||
Long byIp = appUserMapper.selectCount(Wrappers.<AppUser>lambdaQuery()
|
Long byIp = appUserMapper.selectCount(Wrappers.<AppUser>lambdaQuery()
|
||||||
|
|||||||
Reference in new issue
Block a user