需求:
实现一个登录功能,并且2分钟之内只能输入5次错误密码,若输入五次之后还没有输入正确密码,系统将会将该账号锁定1小时
pom.xml
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-devtools
runtime
true
org.springframework.boot
spring-boot-starter-test
test
redis.clients
jedis
3.1.0
org.springframework.boot
spring-boot-starter-data-redis
org.springframework.boot
spring-boot-starter-thymeleaf
yml配置
#redis
redis:
#redis机器ip
hostname: 127.0.0.1
#redis端口
port: 6379
#redis密码
password:
#redis超时时间(毫秒),如果不设置,取默认值2000
timeout: 10000
#最大空闲数
maxIdle: 300
#连接池的最大数据库连接数。设为0表示无限制,如果是jedis 2.4以后用redis.maxTotal
#maxActive=600
#控制一个pool可分配多少个jedis实例,用来替换上面的redis.maxActive,如果是jedis 2.4以后用该属性
maxTotal: 1000
#最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
maxWaitMillis: 1000
#连接的最小空闲时间 默认1800000毫秒(30分钟)
minEvictableIdleTimeMillis: 300000
#每次释放连接的最大数目,默认3
numTestsPerEvictionRun: 1024
#逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
timeBetweenEvictionRunsMillis: 30000
#是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
testOnBorrow: true
#在空闲时检查有效性, 默认false
testWhileIdle: true
#redis集群配置
#spring.cluster.nodes=192.168.1.1:7001,192.168.1.1:7002,192.168.1.1:7003,192.168.1.1:7004,192.168.1.1:7005,192.168.1.1:7006
#spring.cluster.max-redirects=3
#哨兵模式
#sentinel.host1=192.168.1.1
#sentinel.port1=26379
#sentinel.host2=192.168.1.2
#sentinel.port2=26379
spring:
thymeleaf:
cache: false
prefix: classpath:/templates/
suffix: .html
encoding: UTF-8
mode: HTML5
servlet:
bean层:
package com.springbootandredis.springbootredis.bean;
public class User {
String username;
String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
config 配置类
package com.springbootandredis.springbootredis.config;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.io.Serializable;
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfig {
@Bean
public RedisTemplate redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory) {
RedisTemplate template = new RedisTemplate<>();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
controller
package com.springbootandredis.springbootredis.controller;
import com.springbootandredis.springbootredis.Service.ServiceImpl.GetMessageImpl;
import com.springbootandredis.springbootredis.Service.ServiceImpl.LoginServiceImpl;
import com.springbootandredis.springbootredis.bean.User;
import org.hibernate.validator.constraints.pl.REGON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
@Controller
public class userlogin {
@Autowired
LoginServiceImpl loginService;
@Autowired
GetMessageImpl getMessage;
@ResponseBody
@RequestMapping("/login")
public String login(HttpServletRequest request) {
String username = request.getParameter("username");
String password = request.getParameter("password");
User user = new User(username, password);
Map map = loginService.loginUserLock(user); //登录 验证第一层 看用户是否被限制登录
if ((Boolean) map.get("flag")) { //如果为true表示被限制登录
return "登录失败,因" + username + "超过了限制登录次数,已被禁止登录.还剩" + map.get("lockTime") + "分钟";
} else { //表示没有被限制登录 执行 下一步登录逻辑
User user1 = loginService.login(user); //执行登录功能
if (user1 != null) { //表示密码正确 登录成功
/**
* 清空对应的所有key
*/
loginService.DeleteMemory(getMessage.getLoginCountFailKey(user));
loginService.DeleteMemory(getMessage.getLoginTimeLockKey(user));
loginService.DeleteMemory(getMessage.getKeyName(user));
return "登录成功";
} else { //登录不成功 计入登录此时 等逻辑操作
return loginService.loginValdate(user);
}
}
}
@RequestMapping("/tologin")
public String tologin() {
return "login";
}
}
service interface
package com.springbootandredis.springbootredis.Service;
import com.springbootandredis.springbootredis.bean.User;
public interface GetMessage {
String getKeyName(User user);
String getLoginTimeLockKey(User user);
String getLoginCountFailKey(User user);
}
/************分界线***********************************/
package com.springbootandredis.springbootredis.Service;
import com.springbootandredis.springbootredis.bean.User;
import java.util.Map;
public interface LoginService {
/**
* 验证用户登录的账号和密码
* @param user
* @return
*/
User login(User user);
/**
* 用户在2分钟内 仅允许输入错误密码五次,如果超过次数,限制其登录一小时
*
* 给用户详细信息提示
* @param user
* @return
*/
String loginValdata(User user);
/**
* 判断当前的登录用户是否被限制登录
* @param user
* @return
*/
Map loginUserLock(User user);
/**
* 登录不成功的操作(密码错误)
* @param user
* @return
*/
String loginValdate(User user);
/**
* 删除登录失败所存入的键值对
* @param key
* @return
*/
Boolean DeleteMemory(String key);
}
service impl
package com.springbootandredis.springbootredis.Service.ServiceImpl;
import com.springbootandredis.springbootredis.Service.GetMessage;
import com.springbootandredis.springbootredis.bean.User;
import org.springframework.stereotype.Service;
@Service
public class GetMessageImpl implements GetMessage {
@Override
public String getKeyName(User user) {
return "user:"+user.getUsername();
}
@Override
public String getLoginTimeLockKey(User user) {
return "user:loginTime:lock:"+user.getUsername();
}
@Override
public String getLoginCountFailKey(User user) {
return "user:loginCount:fail:"+user.getUsername();
}
}
/******************************************分界线*************************************/
package com.springbootandredis.springbootredis.Service.ServiceImpl;
import com.springbootandredis.springbootredis.Service.LoginService;
import com.springbootandredis.springbootredis.bean.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Service
public class LoginServiceImpl implements LoginService {
@Autowired
GetMessageImpl getMessage;
@Autowired
StringRedisTemplate stringRedisTemplate;
/**
* 验证用户是密码是否正确
*
* @param user
* @return
*/
@Override
public User login(User user) {
if (user.getUsername().equals("dlf")) { //模仿从数据库中根据username查询密码 然后验证密码是否正确
if (user.getPassword().equals("dlf")) {
return user;
}
}
return null;
}
@Override
public String loginValdata(User user) {
return null;
}
/**
* 判断当前登录的用户是否被限制登录
* 查询当前key是否存在,如果存在,就被限制,注意:需要给用户做提示:您当前的用户已被限制,还剩多长时间
* 如果不存在就不被限制
*
* @param user
* @return
*/
@Override
public Map loginUserLock(User user) {
Map map = new HashMap<>();
if (stringRedisTemplate.hasKey(getMessage.getLoginTimeLockKey(user))) {
//如果存在就是用户已经输错了密码五次 被锁定了俩小时
map.put("flag", true); //表示用户已经被锁定
map.put("lockTime", stringRedisTemplate.getExpire(getMessage.getLoginTimeLockKey(user), TimeUnit.MINUTES)); //得到被锁定之后还剩下多少时间 以分钟返回
} else {
map.put("flag", false); //flag 为false 表示用户没有被限制
}
return map;
}
/**
* 登录失败的相应操作(密码错误)
*
* @param user
* @return
*/
@Override
public String loginValdate(User user) {
Integer num = 5;
//记录登录错误次数的key
String key = getMessage.getLoginCountFailKey(user);
if (!stringRedisTemplate.hasKey(key)) { //如果不存在
//是第一次登录失败 次数为1
// userlogincountfile;用户名进行赋值 同时设置失效期2分钟
stringRedisTemplate.opsForValue().set(key, "1", 2, TimeUnit.MINUTES);
return "登录失败,在2分钟内还允许输入错误" + (num - 1) + "次";
} else {
//如果存在
//查询登录失败次数
long loginFilCount = Long.parseLong(stringRedisTemplate.opsForValue().get(key));
if (loginFilCount < (num - 1)) { //代表当前如果登录次数小于4 意思:还有资格继续进行登录
//登录次数+1
stringRedisTemplate.opsForValue().increment(key, 1);
long secends = stringRedisTemplate.getExpire(key, TimeUnit.SECONDS); //返回的是秒
return user.getUsername() + "登录失败,在" + secends + "秒内还允许输入错误" + (num - loginFilCount - 1) + "次";
} else { //超过了指定的登录次数
String lockkey = getMessage.getLoginTimeLockKey(user);
stringRedisTemplate.opsForValue().set(lockkey, "1", 1, TimeUnit.HOURS);
return "因登录失败此时超过" + num + "次,以对其限制登录1小时";
}
}
}
@Override
public Boolean DeleteMemory(String key) {
try {
stringRedisTemplate.delete(key);
} catch (Exception e) {
return false;
}
return true;
}
}