一.思路
1、首先验证登录账号的有效性,是否存在,如果存在继续下一步,不存在直接返回给前端。
2、判断redis中代表用户登录失败的key是否存在。
3、如果存在,则代表这个用户不是第一次因密码错误登录失败了,之后利用定义好的key去获取value,value用字符串存储,转成数值类型加一。
4、如果不存在,但是密码错误,则说明是第一次进来,用定义好的key 给到redis里面,value给成字符串1。
5、下次登录时,便首先判断key是否存在,如存在判断错误次数,超过上限直接返回。
二.准备工作
1.创建一个springboot项目,在pom文件导入依赖包
这里我导入了redis,mysql,hutool工具,lombok,springboot-test的对应的依赖包
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
2.修改yml配置文件
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
#这里用的是我本地的数据库
url: jdbc:mysql://localhost:3306/test
username: root
password: 123456
redis:
database: 0
host: 127.0.0.1
port: 6379
password:
jedis:
pool:
max-active: 200
max-wait: -1
max-idle: 10
min-idle: 0
timeout: 10000
mybatis:
mapper-locations: classpath:mapping/*.xml
# 日志记录输出配置
logging:
level:
cn:
itsource:
mapper: debug
3.创建redisConfig类
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
4.创建公共返回类 BaseResult
public class BaseResult<T> {
private Integer code;
private String message;
private T data;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public BaseResult(){}
public BaseResult(Integer code,String message){
this.code = code;
this.message = message;
}
public BaseResult(Integer code,T data){
this.code = code;
this.data = data;
}
public BaseResult(Integer code,T data,String message){
this.code = code;
this.message = message;
this.data = data;
}
public static <T> BaseResult<T> ok(){
return new BaseResult(ECode.SUCCESS.getCode(),"请求成功");
}
public static <T> BaseResult<T> fail(){
return new BaseResult(ECode.SUCCESS.getCode(),"请求失败");
}
public static <T> BaseResult<T> ok(String message){
return new BaseResult(ECode.SUCCESS.getCode(),message);
}
public static <T> BaseResult<T> ok(T data){
return new BaseResult(ECode.SUCCESS.getCode(),data);
}
public static <T> BaseResult<T> ok(String message,T data){
return new BaseResult(ECode.SUCCESS.getCode(),data,message);
}
public static <T> BaseResult<T> fail(String message){
return new BaseResult(ECode.ERROR.getCode(),message);
}
public static <T> BaseResult<T> fail(T data){
return new BaseResult(ECode.ERROR.getCode(),data);
}
public static <T> BaseResult<T> fail(String message,T data){
return new BaseResult(ECode.ERROR.getCode(),data,message);
}
枚举类
@Getter
@AllArgsConstructor
public enum ECode {
SUCCESS(200,"SUCCESS"),
ERROR(500,"服务器异常"),
LOGIN_ERROR(502011,"用户名或密码不正确");
private Integer code;
private String message;
}
5.自定义异常类
public class LockException extends Exception{
public LockException(){
super("该用户已被锁定10分钟!");
}
public LockException(String message){
super(message);
}
}
public class LoginException extends Exception{
public LoginException(){
}
public LoginException(String message){
super(message);
}
}
6.实体类
@Data
@AllArgsConstructor
@ToString
public class User {
private String username;
private String password;
}
7.入参实体类
@Data
public class UserDto {
private String UUID;
private String username;
private String password;
}
8.service类和mapper
@Autowired
private RedisTemplate<String,Object> redisTemplate;
@Autowired
private LoginMapper loginMapper;
private static final Integer MAX_ERROR = 3;//最大尝试错误次数
@Override
public BaseResult login(UserDto dto) throws Exception{
User user = loginMapper.selectUserInfo(dto);
Integer lock = 1;
ValueOperations<String, Object> opsForValue = redisTemplate.opsForValue();
//从redis里面获取value
Object value = opsForValue.get(dto.getUUID());
if(BeanUtil.isNotEmpty(value)){
if(Integer.parseInt(value.toString())>=MAX_ERROR){
throw new LockException();
}
}
if(BeanUtil.isEmpty(user)){
//锁定次数+1
if(BeanUtil.isNotEmpty(value)) {
lock = Integer.parseInt(value.toString()) + 1;
}
//这里使用uuid用户唯一标识作为key
opsForValue.set(dto.getUUID(), lock,10, TimeUnit.MINUTES);
throw new LoginException("用户名或者密码错误");
}
return BaseResult.ok("登录成功");
}
mapper.xml
<resultMap id="BaseResultMap" type="cn.itsource.pojo.User">
<result column="user_name" jdbcType="VARCHAR" property="username" />
<result column="pass_word" jdbcType="VARCHAR" property="password" />
</resultMap>
<select id="selectUserInfo" resultMap="BaseResultMap">
select user_name,pass_word from user_info
where status = 1
<if test="dto.username!=null and dto.username!=''">
and user_name = #{dto.username}
</if>
<if test="dto.password!=null and dto.password!=''">
and pass_word = #{dto.password}
</if>
</select>
三.测试:junit进行登录测试
第一次到第三次错误登陆:提示用户名或密错误
@Test
void loginTest() {
UserDto dto = new UserDto();
dto.setUUID("123124151341");
dto.setUsername("test");
dto.setPassword("123456");
try{
loginService.login(dto);
}catch (Exception e){
System.out.println(e);
}
}
让我们看看redis里面
?
第四次测试
?
可以看到这里已经成功锁定了用户
本文暂时没有评论,来添加一个吧(●'◡'●)