编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

基于redis实现登录锁定用户需求(redis实现登录次数限制)

wxchong 2024-08-09 11:20:58 开源技术 11 ℃ 0 评论

一.思路

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里面

?

第四次测试

?

可以看到这里已经成功锁定了用户

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表