需求:防止用户进行暴力破解密码
package com.example.demo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.concurrent.TimeUnit; @Controller public class UserController { @Autowired private StringRedisTemplate template; @RequestMapping("/login") @ResponseBody public String start(String username, String password){ String key="user:logincount:"+username; String lockKey="user:lock:"+username; if(template.hasKey(lockKey)){ return "您的账户已经被锁死,请在"+template.getExpire(lockKey)+"秒后进行操作"; }else { if (username.equals("admin") && password.equals("admin")) { return "登陆成功"; } else { if (!template.hasKey(key)) { int value = 1; template.opsForValue().set(key, String.valueOf(value), 120, TimeUnit.SECONDS); } else { template.opsForValue().increment(key); } if (Integer.parseInt(template.opsForValue().get(key)) > 5) { template.opsForValue().set(lockKey, "该账户被锁死", 10000, TimeUnit.SECONDS); return "您在2min内登录超过5次失败,您的帐户将被锁死10000s"; } return "登陆失败"; } } } }
一)发送短信验证码
1)校验手机号是否符合正确的格式
2)校验成功生成验证码
3)保存验证码到session里面
4)发送验证码
二)短信验证码进行登陆注册
1)校验手机号和验证码是否符合正确的格式,如果不符合直接返回错误信息
2)从session中获取到手机号的验证码比对和用户客户端提交过来的验证码是否一致,如果不一致,直接返回错误信息
3)如果验证码校验成功,那么直接根据用户名来进行查询对应的用户信息,如果查询到了,直接将用户信息保存到session里面,如果用户不存在,那么新创建用户保存到session里面
拓展:在hutu里面的BeanUtil.copyProperties方法是把user对象中的所有属性放到UserDemo的一个同名属性里面,并返回UserDemo对象
UserDemo Demo=BeanUtil.copyProperties(user,UserDemo.class);@Controller public class UserController { @Autowired private StringRedisTemplate template; @RequestMapping("/getCode") @ResponseBody public String GetCode (String TelephoneNumber, HttpSession session){ if(!StringUtils.hasLength(TelephoneNumber)){ return "前端输入的手机号为空"; } String code= RandomUtil.randomNumbers(6); System.out.println(code); session.setAttribute("UserCode",code); return "发送验证码成功"; } @RequestMapping("/login") @ResponseBody public String login(String TelephoneNumber,HttpSession session,String UserCode){ //1.校验手机号和验证码 if(!StringUtils.hasLength(TelephoneNumber)){ return "前端输入的手机号为空"; } String code= (String) session.getAttribute("UserCode"); if((code==null||code.equals(""))){ return "您之前没有发送过验证码"; } if(!code.equals(UserCode)){ return "您输入验证码错误"; } //2.从数据库查询用户信息 select * from user where TelephoneNumber=XXXX session.removeAttribute("UserCode"); User user=new User(); user.setUserID(1); user.setUsername("张三"); user.setPassword("李四"); user.setClassID(10); session.setAttribute("user",user); return "登陆成功"; } }
多个session共享的问题:多台Tomact并不是共享session空间的,当请求切换到不同的Tomact服务的时候会出现不同的问题
1)数据共享
2)内存存储,提高读写效率
3)key-value键值对
使用redis来发送验证码
1)验证码保存:因为redis是多台服务器共享的,因为我们要把验证码信息保存在Session里面,但是一定要保证每一名用户所进行发送的验证码保存在redis中的key必须是不一样的,不能使用固定的名称key作为键值对的键,否则拿到大家都是用同一个验证码吗?
key:"user:TelephoneNumber:"+TelephoneNumber
key="user:TemphoneNumber:1367878",value="9090"
2)用户信息保存:因为hash结构可以将对象的每一个单独字段进行保存,并且可以针对单个字段做CRUD
![]()
还要注意一下,当我们进行登陆之后,要向redis中进行存储用户的有效信息,那么必须必须设定过期时间,但是这个过期时间和普通的session又是略有不同
1)session的过期时间:只要超过用户30min不进行访问业务,那么session中的信息就会被销毁,如果用户一直访问Session,那么有效期就是一直是30min
2)但是上面我们使用redis来进行存储用户的登录信息,无论用户是否在进行操作业务逻辑,那么最终redis都会在30min之内进行销毁redis
所以解决方案就是只要用户进行访问我们的业务,我们就需要重新设置用户的信息在redis中的有效期为30min,也就是说是否触发了拦截器,如果不触发拦截器,那么就直接在30min钟内将redis中的用户信息删除
package com.example.demo; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.RandomUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Controller; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpSession; import java.util.HashMap; import java.util.UUID; import java.util.concurrent.TimeUnit; @Controller public class UserController { @Autowired private StringRedisTemplate template; @RequestMapping("/getCode") @ResponseBody public String GetCode (String TelephoneNumber, HttpSession session){ if(!StringUtils.hasLength(TelephoneNumber)){ return "前端输入的手机号为空"; } String code= RandomUtil.randomNumbers(6); System.out.println(code); //生成key,保证每一个人的redis的key都不一样 String key="user:TelephoneNumber:"+TelephoneNumber; template.opsForValue().set(key,code,30, TimeUnit.MINUTES); return "发送验证码成功"; } @RequestMapping("/login") @ResponseBody public String login(String TelephoneNumber,HttpSession session,String UserCode){ //1.校验手机号和验证码 if(!StringUtils.hasLength(TelephoneNumber)){ return "前端输入的手机号为空"; } String key="user:TelephoneNumber:"+TelephoneNumber; String code=template.opsForValue().get(key); if((code==null||code.equals(""))){ return "您之前没有发送过验证码"; } if(!code.equals(UserCode)){ return "您输入验证码错误"; } //2.从数据库查询用户信息 select * from user where TelephoneNumber=XXXX User user=new User(); user.setUserID(1); user.setUsername("张三"); user.setPassword("李四"); user.setClassID(10); //2.1.生成一个token作为令牌,并作为存储在redis中的key String token= "loginUser:token"+UUID.randomUUID(); //2.2.把User对象转化成Hash格式来进行存储 HashMap<String,String> result=new HashMap<>(); result.put("userID",user.getUserID()+""); result.put("username", user.getUsername()); result.put("password", user.getPassword()); result.put("classID",user.getClassID()+""); template.opsForHash().putAll(token,result); template.expire(token,30,TimeUnit.MINUTES); //2.3.向客户端返回token return "登陆成功"+token; } }
@Controller public class UserController { @Autowired private StringRedisTemplate template; @RequestMapping("/getCode") @ResponseBody public String GetCode (String TelephoneNumber, HttpSession session){ if(!StringUtils.hasLength(TelephoneNumber)){ return "前端输入的手机号为空"; } String code= RandomUtil.randomNumbers(6); System.out.println(code); //生成key,保证每一个人的redis的key都不一样 String key="user:TelephoneNumber:"+TelephoneNumber; template.opsForValue().set(key,code,30, TimeUnit.MINUTES); return "发送验证码成功"; } @RequestMapping("/login") @ResponseBody public String login(String TelephoneNumber,HttpSession session,String UserCode){ //1.校验手机号和验证码 if(!StringUtils.hasLength(TelephoneNumber)){ return "前端输入的手机号为空"; } String key="user:TelephoneNumber:"+TelephoneNumber; String code=template.opsForValue().get(key); if((code==null||code.equals(""))){ return "您之前没有发送过验证码"; } if(!code.equals(UserCode)){ return "您输入验证码错误"; } //2.从数据库查询用户信息 select * from user where TelephoneNumber=XXXX User user=new User(); user.setUserID(1); user.setUsername("张三"); user.setPassword("李四"); user.setClassID(10); //2.1.生成一个token作为令牌,并作为存储在redis中的key String token= "loginUser:token"+UUID.randomUUID(); //2.2.把User对象转化成Hash格式来进行存储 HashMap<String,String> result=new HashMap<>(); result.put("userID",user.getUserID()+""); result.put("username", user.getUsername()); result.put("password", user.getPassword()); result.put("classID",user.getClassID()+""); template.opsForHash().putAll(token,result); template.expire(token,30,TimeUnit.MINUTES); //2.3.向客户端返回token return "登陆成功"+token; } }