使用Redis记录错误次数、序列号锁定和冻结时间的实现步骤
在本文中,我们将详细介绍如何使用Redis来记录错误次数、序列号锁定和冻结时间。通过Spring Boot和Spring Data Redis,我们能够轻松实现这些功能。
依赖
首先,确保你的项目中包含以下依赖:
- Spring Boot
- Spring Data Redis
在 pom.xml
中添加以下依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 其他依赖 -->
</dependencies>
配置
在 application.yml
中配置Redis连接:
spring:
redis:
host: localhost
port: 6379
实现
接下来,编写服务类和控制器来实现需求。
Redis配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new StringRedisSerializer());
return template;
}
}
服务类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class ValidationService {
private static final String NAME_ID_KEY = "name_id:";
private static final String CARD_ERROR_KEY = "card_error:";
private static final int LOCK_TIME = 5; // minutes
private static final int MAX_ATTEMPTS = 5;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public boolean validateNameAndId(String name, String id, String correctName, String correctId) {
if (name.equals(correctName) && id.equals(correctId)) {
return true;
}
if (!name.equals(correctName)) {
throw new IllegalArgumentException("姓名输入错误,请重新输入");
}
if (!id.equals(correctId)) {
throw new IllegalArgumentException("工号输入错误,请重新输入");
}
return false;
}
public void validateCard(String serialNumber, String inputCard, String correctCard) {
String cardErrorKey = CARD_ERROR_KEY + serialNumber;
// 检查是否已锁定
if (Boolean.TRUE.equals(redisTemplate.hasKey(cardErrorKey)) && redisTemplate.getExpire(cardErrorKey) > 0) {
throw new IllegalStateException("密码错误次数过多,请稍后再试");
}
// 获取当前错误次数
int attempts = redisTemplate.opsForValue().get(cardErrorKey) == null ? 0 : (int) redisTemplate.opsForValue().get(cardErrorKey);
if (!inputCard.equals(correctCard)) {
attempts++;
if (attempts >= MAX_ATTEMPTS) {
// 超过最大尝试次数,锁定序列号
redisTemplate.opsForValue().set(cardErrorKey, attempts, LOCK_TIME, TimeUnit.MINUTES);
throw new IllegalStateException("密码错误次数过多,请稍后再试");
} else {
// 更新错误次数
redisTemplate.opsForValue().set(cardErrorKey, attempts);
throw new IllegalArgumentException(String.format("卡密已输入错误%d次,超过%d次将锁定", attempts, MAX_ATTEMPTS));
}
} else {
// ��证成功,清除错误次数
redisTemplate.delete(cardErrorKey);
}
}
}
控制器
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/validate")
public class ValidationController {
@Autowired
private ValidationService validationService;
@PostMapping("/name-id")
public String validateNameAndId(@RequestParam String name, @RequestParam String id) {
// 假设 correctName 和 correctId 是从数据库或其他地方获取的正确值
String correctName = "正确的姓名";
String correctId = "正确的工号";
try {
validationService.validateNameAndId(name, id, correctName, correctId);
return "姓名和工号校验通过";
} catch (IllegalArgumentException e) {
return e.getMessage();
}
}
### 控制器
```java
@PostMapping("/card")
public String validateCard(@RequestParam String serialNumber, @RequestParam String inputCard) {
// 假设 correctCard 是从数据库或其他地方获取的正确卡密
String correctCard = "正确的卡密";
try {
validationService.validateCard(serialNumber, inputCard, correctCard);
return "卡密校验通过";
} catch (IllegalArgumentException e) {
return e.getMessage();
} catch (IllegalStateException e) {
return e.getMessage();
}
}
}
详细解析
Redis配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new StringRedisSerializer());
return template;
}
}
解释:
- 配置RedisTemplate来处理Redis操作,设置键和值的序列化器为
StringRedisSerializer
以便于存储和读取字符串类型的数据。
服务类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class ValidationService {
private static final String NAME_ID_KEY = "name_id:";
private static final String CARD_ERROR_KEY = "card_error:";
private static final int LOCK_TIME = 5; // minutes
private static final int MAX_ATTEMPTS = 5;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public boolean validateNameAndId(String name, String id, String correctName, String correctId) {
if (name.equals(correctName) && id.equals(correctId)) {
return true;
}
if (!name.equals(correctName)) {
throw new IllegalArgumentException("姓名输入错误,请重新输入");
}
if (!id.equals(correctId)) {
throw new IllegalArgumentException("工号输入错误,请重新输入");
}
return false;
}
public void validateCard(String serialNumber, String inputCard, String correctCard) {
String cardErrorKey = CARD_ERROR_KEY + serialNumber;
// 检查是否已锁定
if (Boolean.TRUE.equals(redisTemplate.hasKey(cardErrorKey)) && redisTemplate.getExpire(cardErrorKey) > 0) {
throw new IllegalStateException("密码错误次数过多,请稍后再试");
}
// 获取当前错误次数
int attempts = redisTemplate.opsForValue().get(cardErrorKey) == null ? 0 : (int) redisTemplate.opsForValue().get(cardErrorKey);
if (!inputCard.equals(correctCard)) {
attempts++;
if (attempts >= MAX_ATTEMPTS) {
// 超过最大尝试次数,锁定序列号
redisTemplate.opsForValue().set(cardErrorKey, attempts, LOCK_TIME, TimeUnit.MINUTES);
throw new IllegalStateException("密码错误次数过多,请稍后再试");
} else {
// 更新错误次数
redisTemplate.opsForValue().set(cardErrorKey, attempts);
throw new IllegalArgumentException(String.format("卡密已输入错误%d次,超过%d次将锁定", attempts, MAX_ATTEMPTS));
}
} else {
// 验证成功,清除错误次数
redisTemplate.delete(cardErrorKey);
}
}
}
解释:
validateNameAndId
方法:校验用户输入的姓名和工号是否与正确的姓名和工号匹配。如果不匹配,抛出适当的异常。validateCard
方法:校验卡密是否正确。使用Redis记录每个序列号的错误次数,并在错误次数达到最大尝试次数时锁定序列号一定时间。attempts
变量用于记录当前错误次数。如果Redis中不存在该键,则初始化为0。- 如果输入的卡密不正确,错误次数增加:
- 如果错误次数达到或超过最大尝试次数(
MAX_ATTEMPTS
),则设置锁定时间(LOCK_TIME
),并抛出IllegalStateException
。 - 否则,更新错误次数并抛出
IllegalArgumentException
,提示错误次数。
- 如果错误次数达到或超过最大尝试次数(