【三】Springboot+Redis实现密码次数限制

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_19301269/article/details/78728127

继前两篇权限控制+gif验证码+记住我功能+信息查询缓存之后,这里介绍如何用redis实现密码次数限制。

权限控制+gif链接

Redis信息缓存

1. 需要实现的功能

为了防止密码被暴力破解,采用redis对用户登录次数进行计数,如果超过5,则把用户锁定1小时,不让用户登录;

前端通过ajax告诉用户还有几次输错密码的机会;

2. 实现的domo展示

首先是登陆界面,之后我会连续让admin账号用错误的密码重复登陆


采用错误密码进行登陆,直到机会都用完


之后,再次尝试登陆时,会将用户锁定一个小时,并在session里保存该用户,每次登陆都会判断该用户是否被锁定,如果被锁定,就跳转到锁定页面,直到一个小时候,用户才能再次访问登陆界面


3.代码部分,在之前的基础上进行修改

主要是controller部分进行判断,在redis存入两个key,一个是对用户登录进行计数,一个是储存用户是不是lock,代码如下

import java.util.HashMap;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.session.Session;
import org.springframework.beans.factory.annotation.Autowired;
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.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class HomeController {
	@Autowired
	private StringRedisTemplate stringRedisTemplate;
    //用户登录次数计数  redisKey 前缀  
    private String SHIRO_LOGIN_COUNT = "shiro-login-count";  
    //用户登录是否被锁定    一小时 redisKey 前缀  
    private String SHIRO_IS_LOCK = "shiro-is-lock";  
    private int lefttime=5;

	@RequestMapping({"/","/index","/error"})
	public String index(){
		return "/index";
	}
	@RequestMapping(value="/lock",method=RequestMethod.GET)
	public String lock(){
		return "/lock";
	}
	//每次登陆都对用户进行判断,如果被锁定了,直接跳转到lock
	@RequestMapping(value="/login",method=RequestMethod.GET)
	public String login(){
		Session session = SecurityUtils.getSubject().getSession();
		String islockString=(String) session.getAttribute("user");
		if(islockString==null){
			return "login";
		}else{
		return "lock";
		}
	}
	
	// 登录提交地址和applicationontext-shiro.xml配置的loginurl一致。 (配置文件方式的说法)
//		@RequestMapping(value="/login",method=RequestMethod.POST)
//		public String login(HttpServletRequest request, Map<String, Object> map) throws Exception {
//			System.out.println("HomeController.login()");
//			// 登录失败从request中获取shiro处理的异常信息。
//			// shiroLoginFailure:就是shiro异常类的全类名.
//			String exception = (String) request.getAttribute("shiroLoginFailure");
//			
//			System.out.println("exception=" + exception);
//			String msg = "";
//			if (exception != null) {
//				if (UnknownAccountException.class.getName().equals(exception)) {
//					System.out.println("UnknownAccountException -- > 账号不存在:");
//					msg = "UnknownAccountException -- > 账号不存在:";
//				} else if (IncorrectCredentialsException.class.getName().equals(exception)) {
//					System.out.println("IncorrectCredentialsException -- > 密码不正确:");
//					msg = "IncorrectCredentialsException -- > 密码不正确:";
//				} else if ("kaptchaValidateFailed".equals(exception)) {
//					System.out.println("kaptchaValidateFailed -- > 验证码错误");
//					msg = "kaptchaValidateFailed -- > 验证码错误";
//				} else {
//					msg = "else >> "+exception;
//					System.out.println("else -- >" + exception);
//				}
//			}
//			map.put("msg", msg);
//			// 此方法不处理登录成功,由shiro进行处理.
//			return "/login";
//		}
		@RequestMapping(value="/ajaxLogin",method=RequestMethod.POST)
		@ResponseBody
		public Map<String,Object> submitLogin(@RequestParam("username")String username,@RequestParam("password") String password,@RequestParam("vcode")String vcode,@RequestParam("rememberMe")Boolean rememberMe) {
		    Map<String, Object> resultMap = new LinkedHashMap<String, Object>();
		    Map<String, String> map=new HashMap<String, String>();

		    if(vcode==null||vcode==""){
		        resultMap.put("status", 500);
		        resultMap.put("message", "验证码不能为空!");
		        return resultMap;
		    }

		    Session session = SecurityUtils.getSubject().getSession();
		    //转化成小写字母
		    vcode = vcode.toLowerCase();
		    String v = (String) session.getAttribute("_code");
		    //还可以读取一次后把验证码清空,这样每次登录都必须获取验证码
		    //session.removeAttribute("_come");
		    if(!vcode.equals(v)){
		        resultMap.put("status", 500);
		        resultMap.put("message", "验证码错误!");
		        return resultMap;
		    }
		    ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue();  
	        opsForValue.increment(SHIRO_LOGIN_COUNT+username, 1);  
	        //计数大于5时,设置用户被锁定一小时  
	        if(Integer.parseInt(opsForValue.get(SHIRO_LOGIN_COUNT+username))>5){  
	            opsForValue.set(SHIRO_IS_LOCK+username, "LOCK");  
	            stringRedisTemplate.expire(SHIRO_IS_LOCK+username, 1, TimeUnit.HOURS);  
	        }  


		    try {
		        if ("LOCK".equals(opsForValue.get(SHIRO_IS_LOCK+username))){    
		        	session.setAttribute("user", "lock");
		        	session.setTimeout(60);
		            throw new LockedAccountException();
		        }  
		        UsernamePasswordToken token = new UsernamePasswordToken(username, password,rememberMe);
		        SecurityUtils.getSubject().login(token);

		        resultMap.put("status", 200);
		        resultMap.put("message", "登录成功");
		        opsForValue.set(SHIRO_LOGIN_COUNT+username, "0");
		        opsForValue.set(SHIRO_IS_LOCK+username, "UNLOCK");
		        lefttime=5;
		        map.put("user",username);
		        
		    }catch (LockedAccountException e) {
		        resultMap.put("status", 400);
		        resultMap.put("message", "您已经被锁定1小时!");
			}catch (UnknownAccountException e) {
		        resultMap.put("status", 300);
		        resultMap.put("message", "账号不存在!");
		    }catch(IncorrectCredentialsException e1){
		    	lefttime--;
		        resultMap.put("status", 100);
		        resultMap.put("message", "密码错误!");
		        resultMap.put("lefttime", lefttime);
		    }catch (Exception e) {
		        resultMap.put("status", 500);
		        resultMap.put("message", "账号不存在");
			}
		    return resultMap;
		}
	
}

然后是前端login.html部分,进行ajax交互

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />

<title>LoginDemo</title>
<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta>
    <meta http-equiv="X-UA-Compatible" content="IE=edge"></meta>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" ></meta>
    <meta name="renderer" content="webkit"></meta>
    <title>登录</title>
	
    <link rel="stylesheet" href="css/base.css"></link>
    <link rel="stylesheet" href="css/style.css"></link>
</head>
<script>
/* $(function(){
    $("#ajaxLogin").click(function() {
        var username = $("#username").val();
        var password = $("#password").val();
        var vcode = $("#vcode").val();
        var rememberMe =$('#rememberMe').is(':checked');
        $.post("ajaxLogin", {
            "username" : username,
            "password" : password,
            "vcode" : vcode,
            "rememberMe" : rememberMe
        }, function(result) {
            if (result.status == 200) {
                location.href = "/index";
            } else {
                $("#erro").html(result.message);
            }
        });
    });
}); */
function sendAjax()
{
        var username = $("#username").val();
        var password = $("#password").val();
        var vcode = $("#vcode").val();
        var rememberMe =$('#rememberMe').is(':checked');
 $.ajax(
    {
        url:"/ajaxLogin",
        data:{"username":username,"password":password,"vcode":vcode,"rememberMe":rememberMe},
        type:"post",
        dataType:"json",
        success:function(data)
        { 
             if(data.status==200){
             
              location.href = "/index";
             }else if(data.status==400){
              location.href = "/lock";
             }else if(data.status==100){
            $("#erro1").html("密码错误,您还有:"+data.lefttime+" 次机会");
            }else if(data.status==500){
            $("#erro1").html(data.message);
            }else if(data.status==300){
            $("#erro1").html(data.message);
            }else{
            }

        },
        error: function() {
            $("#erro").html("登录失败");
          }
    }); 
    }
    function change(){
     $("#codePic").attr("src","/getGifCode?flag="+Math.random());   
    }
</script>
<body>
		
    <div class="bg"></div>
    <div class="container">
        <div class="line bouncein">
            <div class="xs6 xm4 xs3-move xm4-move">
                <div style="height:150px;"></div>
                <div class="media media-y margin-big-bottom">
                </div>
                    <div class="panel loginbox">
                        <div class="text-center margin-big padding-big-top">
                            <h1>后台管理中心</h1>
                            <h4 style="color:#FF0000" id="erro1"></h4> 
                        </div>
                        <div class="panel-body" style="padding:30px; padding-bottom:10px; padding-top:10px;">
                            <div class="form-group">
                                <div class="field field-icon-right">
                                    <input type="text" class="input input-big"  name="username" id="username" placeholder="登录账号" />
                                    <span class="icon icon-user margin-small"></span>
                                </div>
                            </div>
                            <div class="form-group">
                                <div class="field field-icon-right">
                                    <input type="password" class="input input-big" name="password" id="password"  placeholder="登录密码" />
                                    <span class="icon icon-key margin-small"></span>
                                </div>
                            </div>
                            <div class="form-group">
                                <div class="field">
                                    <input type="text" class="input input-big" id="vcode" name="vcode" placeholder="填写右侧的验证码" />
                                    <img id="codePic" src="http://localhost:8080/getGifCode" οnclick="change()" alt="" width="100" height="32" class="passcode" style="height:43px;cursor:pointer;" />

                                </div>
                            </div>
                            <P><input type="checkbox" name="rememberMe" id="rememberMe" />记住我</P>
                        </div>
                        <div style="padding:30px;">
                            <input type="button" οnclick="sendAjax()" value="登录" id="ajaxLogin" class="button button-block bg-main text-big input-big" />
                        </div>
                    </div>
            </div>
        </div>
    </div>

</body>
</html>

到这里,代码部分就完成了,注释都写在代码里了。


展开阅读全文
博主设置当前文章不允许评论。

没有更多推荐了,返回首页