ruoyi-auth/src/main/java/com/ruoyi/auth/service/LoginRateLimitService.java
@@ -6,41 +6,45 @@
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.ip.IpUtils;
import com.ruoyi.common.redis.service.RedisService;
/**
 * 登录频率限制服务
 * 登录频率限制服务(账号维度)
 */
@Component
public class LoginRateLimitService
{
    private static final String UNKNOWN = "unknown";
    private static final String SESSION_HEADER = "X-Login-Session";
    @Autowired
    private RedisService redisService;
    /**
     * 按 IP、账号、登录会话三个维度限制登录尝试频率。
     * 按账号维度限制登录尝试频率。
     * IP、登录会话维度已由网关 LoginRateLimitFilter 在验证码校验之前处理
     * (验证码错误的请求不会到达本服务,若在 auth 重复计数会与网关双重计数)。
     */
    public void checkLoginRate(String username, HttpServletRequest request)
    public void checkLoginRate(String username)
    {
        String normalizedUsername = StringUtils.defaultString(username).trim().toLowerCase(Locale.ROOT);
        String ip = StringUtils.defaultIfEmpty(IpUtils.getIpAddr(request), UNKNOWN);
        String sessionId = resolveSessionId(request);
        checkDimension("ip", ip);
        checkDimension("account", normalizedUsername);
        checkDimension("session", sessionId);
    }
    /**
     * 清除账号维度的登录限流计数(登录成功后调用)。
     */
    public void clearAccountRecord(String username)
    {
        String normalizedUsername = StringUtils.defaultString(username).trim().toLowerCase(Locale.ROOT);
        String key = CacheConstants.LOGIN_RATE_LIMIT_KEY + "account:" + digest(normalizedUsername);
        if (redisService.hasKey(key))
        {
            redisService.deleteObject(key);
        }
    }
    private void checkDimension(String dimension, String identity)
@@ -53,17 +57,6 @@
            long retryAfter = remainingSeconds > 0 ? remainingSeconds : CacheConstants.LOGIN_RATE_LIMIT_WINDOW;
            throw new ServiceException(String.format("登录尝试过于频繁,请%s秒后重试", retryAfter));
        }
    }
    private String resolveSessionId(HttpServletRequest request)
    {
        String sessionId = request.getHeader(SESSION_HEADER);
        if (StringUtils.isNotEmpty(sessionId))
        {
            return sessionId;
        }
        String userAgent = StringUtils.defaultString(request.getHeader("User-Agent"));
        return IpUtils.getIpAddr(request) + "|" + userAgent;
    }
    private String digest(String value)