情景:发送手机验证码或者邮箱验证码时限制规则:一分钟只可以发一次,一天内也有次数限制。以防止恶意访问,降低服务器压力。
解决思路:获取用户ip地址,判断此ip是否首次访问,如果是首次访问,在redis创建minKey,dayKey.并设置minKey过期60s,dayKey为86400s,也就是24H。首次访问则次数加一。超过1次或一天超过限制次数时,禁止访问。
1.获取用户真实ip地址。
/**
* 自定义访问对象工具类
* 获取对象的IP地址等信息
*/
public class GetIpAddressUtil {
/**
* 获取用户真实IP地址,不使用request.getRemoteAddr();的原因是有可能用户使用了代理软件方式避免真实IP地址,
*
* 可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP值,究竟哪个才是真正的用户端的真实IP呢?
* 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。
*
* 如:X-Forwarded-For:192.168.1.110, 192.168.1.120, 192.168.1.130,
* 192.168.1.100
* 用户真实IP为: 192.168.1.110
* @param request
* @return
*/
public static String getIpInfo(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
} else if (ip.length() > 15) {
String[] ips = ip.split(",");
for (int index = 0; index < ips.length; index++) {
String strIp = (String) ips[index];
if (!("unknown".equalsIgnoreCase(strIp))) {
ip = strIp;
break;
}
}
}
return ip;
}
}
2.Redis配置
ublic class MyJedisPool {
private final static Logger logger = LoggerFactory.getLogger(MyJedisPool.class);
private static JedisPool jedisPool = null;
//redis服务配置
public static final String Redis_IP = "192.168.xx.xx";
public static final int Redis_PORT =6379;
public static final String Redis_jedisUser ="";//密码
public static final int ExpireSeconds = 3600;
//静态代码初始化池配置
static {
try{
//创建jedis池配置实例
JedisPoolConfig config = new JedisPoolConfig();
//设置池配置项值
config.setMaxTotal(20);
config.setMaxIdle(10);
config.setMaxWaitMillis(3000);
config.setTestOnBorrow(true);
config.setTestOnReturn(false);
//根据配置实例化jedis池
//jedisPool = new JedisPool(config,Redis_IP,Redis_PORT,1000*2,Redis_jedisUser);
jedisPool = new JedisPool(config,SystemFileConfig.get("Redis_IP"),
Integer.parseInt(SystemFileConfig.get("Redis_PORT")),
1000*2,
SystemFileConfig.get("Redis_jedisUser"),
Integer.parseInt(SystemFileConfig.get("Redis_database")));
}catch (Exception e) {
logger.info("redis连接池异常",e);
}
}
/**获得jedis对象*/
public static Jedis getJedisObject(){
return jedisPool.getResource();
}
/**归还jedis对象*/
public static void returnJedisOjbect(Jedis jedis){
if (jedis != null) {
jedis.close();
}
}
}
3.操作redis完成次数限制
String userIpAddr = GetIpAddressUtil.getIpInfo(request);
Jedis jedis = MyJedisPool.getJedisObject();//获取jedis链接对象
boolean flag = true;
flag = limitSendCount(userIpAddr,jedis);
System.out.println(flag); //根据flag判断是否超出限制
if (jedis != null) {
jedis.close(); //用完关闭jedis资源
}
//判断是否超出限制
public boolean limitSendCount(String ip, Jedis jedis) {
String value = jedis.get(ip+"MinLimte");
String dayValue = jedis.get(ip+"DayLimte");
if(value==null){
jedis.set(ip+"MinLimte", "1");
jedis.expire(ip+"MinLimte", 60);//设置过期时间60秒
if(dayValue==null) {
jedis.set(ip+"DayLimte", "1");
jedis.expire(ip+"DayLimte", 86400);//设置过期时间24hours
}else {
jedis.incr(ip+"DayLimte"); //加一次
int parseIntDay = Integer.parseInt(dayValue);
if(parseIntDay>20){
System.out.println("访问受限!!!!");
return false;
}
}
return true;
}else{
int parseInt = Integer.parseInt(value);
int parseIntDay = Integer.parseInt(dayValue);
60秒内访问超过1次,或者一天超过20次,就禁止访问
if(parseInt>=1 || parseIntDay>20){
System.out.println("访问受限!!!!");
return false;
}
jedis.incr(ip+"MinLimte");
jedis.incr(ip+"DayLimte");
}
return true;
}
4.以上后端验证次数已完成。如果前后不分离,前端需要在点击按钮后禁止点击,按钮显示倒计时直至为0s方可再次点击时,前端js如下:
//发送成功后60秒内不可再次发送
var id = 60;
//定时执行
var timeing = setInterval(function() {
id = id - 1;
$('#sendVcode').html(id + 's');
}, 1000);
//延迟执行
window.setTimeout(function() {
//结束定时,恢复按钮可用
clearInterval(timeing);
$('#sendVcode').html('点击发送Send').removeAttr("disabled");
$("#sendVcode").removeClass("sendclass2");//设置自己的按钮样式
$("#sendVcode").addClass("sendclass1");
}, 60000);
本文介绍了一种基于Redis的验证码发送频率限制策略,包括获取用户真实IP地址的方法、Redis配置及操作实现,以及前端按钮禁用倒计时逻辑,确保系统的稳定性和安全性。
1331

被折叠的 条评论
为什么被折叠?



