避免频繁请求登录的一种解决方案(无验证码方式)

本文介绍了一种不依赖验证码的登录频率限制策略,通过记录IP地址和请求时间,当请求过于频繁时阻止登录并提示用户。在超过20次连续请求后,系统会将IP加入黑名单,2小时后才可尝试登录。实现代码基于SpringSecurity,使用Map存储IP登录次数,生产环境可替换为Redis等缓存。
摘要由CSDN通过智能技术生成

最近在重构之前自己的一个网站系统,在进行到登录认证这块想将输入验证码的方式去掉,改为动态的根据请求频率来限制,因为输入验证码的体验其实并不是很好。以下的解决方案只是我目前想到的一种,就是通过登录的IP和频率来检测。

演示视频

防止频繁请求登录的种解决方法(不使用验证码实现)

解决方法

整体的方案分为以下几步:

  1. 用户请求登录接口时将请求的IP地址和请求的时间记录,对应的时间加500毫秒
  2. 判断该IP地址对应的时间和当前时间的大小,如果大于当前时间则表明请求频繁,因为只有频繁的请求后对应的时间才会累积的超过当前时间
  3. 当请求频繁次数超过20次时将IP地址的时间大小加2小时,并提示涉嫌暴力登录,2小时后重试

代码实现

下面的代码是基于Map来进行IP地址和时间的缓存的,在生产环境中可替代为redis等中间缓存。另外我这里的安全认证框架是使用的Spring Security,下面的代码是继承了UsernamePasswordAuthenticationFilter后重写了attemptAuthentication方法实现的,里面除了实现登录限制外还有兼容json数据请求登录的代码,可忽略

package vip.huhailong.devcat.security.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import vip.huhailong.devcat.common.exception.LoginCountException;
import vip.huhailong.devcat.common.util.HttpUtil;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

@Slf4j
public class SysAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private static Map<String,Long> loginIpCountMap = new HashMap<>(); //记录ip登录次数,用来限制频繁登录

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        String ipAddress = HttpUtil.getIpAddress(request);
        long currentTimeMillis = System.currentTimeMillis();
        Long loginLoginTime = loginIpCountMap.getOrDefault(ipAddress,currentTimeMillis);
        long loginCount = (loginLoginTime - currentTimeMillis)/1000;  //登录次数
        //如果一个IP连续操作频繁20次则限制其2小时后才可以继续操作
        if(loginCount > 20){
            log.info("ip:{},already into blacklist, it's will quit blacklist after two hours",ipAddress);
            loginIpCountMap.put(ipAddress,currentTimeMillis+1000*60*60*2);
            throw new LoginCountException("您的操作涉嫌暴力请求,登录操作已被禁止2小时");
        }
        if(loginLoginTime > currentTimeMillis){
            loginIpCountMap.put(ipAddress,loginIpCountMap.getOrDefault(ipAddress,currentTimeMillis)+1000);
            log.info("ip:{},login count is too much, count:{}",ipAddress, loginCount);
            throw new LoginCountException("登录操作频繁,请稍后再试");
        }else{
            loginIpCountMap.put(ipAddress,currentTimeMillis+1000);
        }
        //以下代码是之前的逻辑,与本次演示的请求频率限制无关,可忽略
        if (!request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        }
        if(request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)) {
            String username = null;
            String password = null;
            try{
                Map<String,String> map = new ObjectMapper().readValue(request.getInputStream(), Map.class);
                username = Optional.ofNullable(map.get("username")).orElse("");
                password = Optional.ofNullable(map.get("password")).orElse("");
            } catch (IOException e){
                logger.error(e.getMessage());
            }
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username,password);
            setDetails(request,authenticationToken);
            return this.getAuthenticationManager().authenticate(authenticationToken);
        }
        return super.attemptAuthentication(request, response);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@胡海龙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值