黑马的登录模块加手机号黑名单
借鉴了一个博主的,我把完整代码实现,测试没问题
下面考虑一个问题,用户请求发送验证码时,服务器这边的逻辑是直接调用相关接口(这里需要运营商提供增值服务),将验证码发给用户,同时服务器端也会存储一份验证码。但是如果有人恶意使用这项功能,大量发送无效验证码,那么就会给服务器带来压力,同时增加公司的开销,这正是我们愿意看到的(bushi)。
所以这里需要对请求中的手机号获取验证码进行次数限制。在我们的日常使用中,一般一分钟只能获取一次验证码,这里的实现思路很简单,可以称为使用锁的思想,每次有请求想要获取验证码时先检查redis中是否存在对应的锁,如果存在,则返回失败,如果不存在,则生成验证码并在redis中设置一个过期时间为一分钟的锁。同时还可以实现一个黑名单功能,限制一个手机号一小时内只能获取3次验证码,超过次数则拉入黑名单,24小时后从黑名单中移除。
public class RedisConstants {
// 1分钟限频Key前缀(格式:verify:limit:手机号)
public static final String VERIFY_LIMIT_KEY = "verify:limit:";
// 1分钟限频过期时间(单位:秒)
public static final Long VERIFY_LIMIT_TTL = 60L;
// 请求次数统计Key前缀(格式:verify:count:手机号)
public static final String VERIFY_COUNT_KEY = "verify:count:";
// 次数统计过期时间(比如1小时,避免长期占用内存)
public static final Long VERIFY_COUNT_TTL = 3600L;
// 黑名单Key前缀(格式:verify:black:手机号)
public static final String VERIFY_BLACK_KEY = "verify:black:";
// 黑名单过期时间(24小时,单位:秒)
public static final Long VERIFY_BLACK_TTL = 86400L;
}
@Override
public Result sendCode(String phone, HttpSession session) {
//1,校验手机号
if (RegexUtils.isPhoneInvalid(phone)) {
//2,不符合,返回错误信息
return Result.fail("手机格式错误!");
}
String blackKey = RedisConstants.VERIFY_BLACK_KEY + phone;
if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(blackKey))) {
// 已在黑名单,拒绝请求
return Result.fail("已在黑名单,拒绝请求,24小时之后再访问,亲!");
}
// 步骤2:判断是否触发“1分钟限1次”
String limitKey = RedisConstants.VERIFY_LIMIT_KEY + phone;
if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(limitKey))) {
// 1分钟内已请求过,拒绝
return Result.fail("1分钟内访问过一次");
}
// 步骤3:统计请求次数,判断是否超3次
String countKey = RedisConstants.VERIFY_COUNT_KEY + phone;
// 自增请求次数(初始为1)
Long count = stringRedisTemplate.opsForValue().increment(countKey);
// 设置过期时间(仅第一次自增时设置)
if (count == 1) {
stringRedisTemplate.expire(countKey, RedisConstants.VERIFY_COUNT_TTL, TimeUnit.SECONDS);
}
// 步骤4:次数超3次 → 加入黑名单
if (count > 2) { //调试的这里是2,因为这个逻辑在验证的后面
stringRedisTemplate.opsForValue().set(blackKey, "1", RedisConstants.VERIFY_BLACK_TTL, TimeUnit.SECONDS);
// 清除次数统计和限频Key(可选,避免干扰)
stringRedisTemplate.delete(countKey);
stringRedisTemplate.delete(limitKey);
}
// 步骤5:通过所有校验 → 设置1分钟限频锁
stringRedisTemplate.opsForValue().set(limitKey, "1", RedisConstants.VERIFY_LIMIT_TTL, TimeUnit.SECONDS);
// 后续逻辑:生成验证码、调用运营商接口发送等
//3,符合,生成验证码
String code = RandomUtil.randomNumbers(6);
//4保存验证码到redis 有效期
stringRedisTemplate.opsForValue().set("login:code"+phone,code,2, TimeUnit.MINUTES);
//5,发送验证码
log.debug("发送短信验证码成功,验证码:{}",code);
//返回ok
return Result.ok();
}
下面考虑一个问题,用户请求发送验证码时,服务器这边的逻辑是直接调用相关接口(这里需要运营商提供增值服务),将验证码发给用户,同时服务器端也会存储一份验证码。但是如果有人恶意使用这项功能,大量发送无效验证码,那么就会给服务器带来压力,同时增加公司的开销,这正是我们愿意看到的(bushi)。
所以这里需要对请求中的手机号获取验证码进行次数限制。在我们的日常使用中,一般一分钟只能获取一次验证码,这里的实现思路很简单,可以称为使用锁的思想,每次有请求想要获取验证码时先检查redis中是否存在对应的锁,如果存在,则返回失败,如果不存在,则生成验证码并在redis中设置一个过期时间为一分钟的锁。同时还可以实现一个黑名单功能,限制一个手机号一小时内只能获取3次验证码,超过次数则拉入黑名单,24小时后从黑名单中移除。
public class RedisConstants {
// 1分钟限频Key前缀(格式:verify:limit:手机号)
public static final String VERIFY_LIMIT_KEY = "verify:limit:";
// 1分钟限频过期时间(单位:秒)
public static final Long VERIFY_LIMIT_TTL = 60L;
// 请求次数统计Key前缀(格式:verify:count:手机号)
public static final String VERIFY_COUNT_KEY = "verify:count:";
// 次数统计过期时间(比如1小时,避免长期占用内存)
public static final Long VERIFY_COUNT_TTL = 3600L;
// 黑名单Key前缀(格式:verify:black:手机号)
public static final String VERIFY_BLACK_KEY = "verify:black:";
// 黑名单过期时间(24小时,单位:秒)
public static final Long VERIFY_BLACK_TTL = 86400L;
}
@Override
public Result sendCode(String phone, HttpSession session) {
//1,校验手机号
if (RegexUtils.isPhoneInvalid(phone)) {
//2,不符合,返回错误信息
return Result.fail("手机格式错误!");
}
String blackKey = RedisConstants.VERIFY_BLACK_KEY + phone;
if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(blackKey))) {
// 已在黑名单,拒绝请求
return Result.fail("已在黑名单,拒绝请求,24小时之后再访问,亲!");
}
// 步骤2:判断是否触发“1分钟限1次”
String limitKey = RedisConstants.VERIFY_LIMIT_KEY + phone;
if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(limitKey))) {
// 1分钟内已请求过,拒绝
return Result.fail("1分钟内访问过一次");
}
// 步骤3:统计请求次数,判断是否超3次
String countKey = RedisConstants.VERIFY_COUNT_KEY + phone;
// 自增请求次数(初始为1)
Long count = stringRedisTemplate.opsForValue().increment(countKey);
// 设置过期时间(仅第一次自增时设置)
if (count == 1) {
stringRedisTemplate.expire(countKey, RedisConstants.VERIFY_COUNT_TTL, TimeUnit.SECONDS);
}
// 步骤4:次数超3次 → 加入黑名单
if (count > 2) { //调试的这里是2,因为这个逻辑在验证的后面
stringRedisTemplate.opsForValue().set(blackKey, "1", RedisConstants.VERIFY_BLACK_TTL, TimeUnit.SECONDS);
// 清除次数统计和限频Key(可选,避免干扰)
stringRedisTemplate.delete(countKey);
stringRedisTemplate.delete(limitKey);
}
// 步骤5:通过所有校验 → 设置1分钟限频锁
stringRedisTemplate.opsForValue().set(limitKey, "1", RedisConstants.VERIFY_LIMIT_TTL, TimeUnit.SECONDS);
// 后续逻辑:生成验证码、调用运营商接口发送等
//3,符合,生成验证码
String code = RandomUtil.randomNumbers(6);
//4保存验证码到redis 有效期
stringRedisTemplate.opsForValue().set("login:code"+phone,code,2, TimeUnit.MINUTES);
//5,发送验证码
log.debug("发送短信验证码成功,验证码:{}",code);
//返回ok
return Result.ok();
}
全部评论
好加,多多练习,加油
黑马点评的登录模块
相关推荐
01-05 10:03
门头沟学院 后端工程师 点赞 评论 收藏
分享
葛明珠:被动打杂真的是实习的坑,主动找问题 + 带方案沟通,才是实习的正确打开方式
点赞 评论 收藏
分享
01-04 14:26
北京交通大学 Java 点赞 评论 收藏
分享
点赞 评论 收藏
分享
01-06 18:56
门头沟学院 Java 点赞 评论 收藏
分享
