三次输错密码后,系统是怎么做到不让我继续尝试的?

1故事背景

忘记密码这件事,相信绝大多数人都遇到过,输一次错一次,错到几次以上,就不允许你继续尝试了。

但当你尝试重置密码,又发现新密码不能和原密码重复:

图片

图片

相信此刻心情只能用一张图形容:

图片

图片

虽然,但是,密码还是很重要的,顺便我有了一个问题:三次输错密码后,系统是怎么做到不让我继续尝试的?

2我想了想,有如下几个问题需要搞定

  • 是只有输错密码才锁定,还是账户名和密码任何一个输错就锁定?

  • 输错之后也不是完全冻结,为啥隔了几分钟又可以重新输了?

  • 技术栈到底麻不麻烦?

去网上搜了搜,也问了下ChatGPT,找到一套解决方案:SpringBoot+Redis+Lua脚本。

这套方案也不算新,很早就有人在用了,不过难得是自己想到的问题和解法,就记录一下吧。

顺便回答一下上面的三个问题:

  • 锁定的是IP,不是输入的账户名或者密码,也就是说任一一个输错3次就会被锁定

  • Redis的Lua脚本中实现了key过期策略,当key消失时锁定自然也就消失了

  • 技术栈同SpringBoot+Redis+Lua脚本

3那么自己动手实现一下

前端部分

首先写一个账密输入页面,使用很简单HTML加表单提交

<!DOCTYPE html>
<html>
<head>
 <title>登录页面</title>
 <style>
  body {
   background-color: #F5F5F5;
  }
  form {
   width: 300px;
   margin: 0 auto;
   margin-top: 100px;
   padding: 20px;
   background-color: white;
   border-radius: 5px;
   box-shadow: 0 0 10px rgba(0,0,0,0.2);
  }
  label {
   display: block;
   margin-bottom: 10px;
  }
  input[type="text"], input[type="password"] {
   border: none;
   padding: 10px;
   margin-bottom: 20px;
   border-radius: 5px;
   box-shadow: 0 0 5px rgba(0,0,0,0.1);
   width: 100%;
   box-sizing: border-box;
   font-size: 16px;
  }
  input[type="submit"] {
   background-color: #30B0F0;
   color: white;
   border: none;
   padding: 10px;
   border-radius: 5px;
   box-shadow: 0 0 5px rgba(0,0,0,0.1);
   width: 100%;
   font-size: 16px;
   cursor: pointer;
  }
  input[type="submit"]:hover {
   background-color: #1C90D6;
  }
 </style>
</head>
<body>
 <form action="http://localhost:8080/login" method="get">
  <label for="username">用户名</label>
  <input type="text" id="username" name="username" placeholder="请输入用户名" required>
  <label for="password">密码</label>
  <input type="password" id="password" name="password" placeholder="请输入密码" required>
  <input type="submit" value="登录">
 </form>
</body>
</html>

效果如下:

图片

图片

后端部分

技术选型分析

首先我们画一个流程图来分析一下这个登录限制流程

图片

图片

  • 从流程图上看,首先访问次数的统计与判断不是在登录逻辑执行后,而是执行前就加1了;

  • 其次登录逻辑的成功与失败并不会影响到次数的统计;

  • 最后还有一点流程图上没有体现出来,这个次数的统计是有过期时间的,当过期之后又可以重新登录了。

那为什么是Redis+Lua脚本呢?

Redis的选择不难看出,这个流程比较重要的是存在一个用来计数的变量,这个变量既要满足分布式读写需求,还要满足全局递增或递减的需求,那Redis的incr方法是最优选了。

那为什么需要Lua脚本呢?流程上在验证用户操作前有些操作,如图:

图片

图片

这里至少有3步Redis的操作,get、incr、expire,如果全放到应用里面来操作,有点慢且浪费资源。

Lua脚本的优点如下:

  • 减少网络开销。 可以将多个请求通过脚本的形式一次发送,减少网络时延。

  • 原子操作。 Redis会将整个脚本作为一个整体执行,中间不会被其他请求插入。因此在脚本运行过程中无需担心会出现竞态条件,无需使用事务。

  • 复用。 客户端发送的脚本会永久存在redis中,这样其他客户端可以复用这一脚本,而不需要使用代码完成相同的逻辑。

最后为了增加功能的复用性,我打算使用Java注解的方式实现这个功能。

代码实现

项目结构如下

图片

图片

配置文件

pom.xml

<?xml version="1.0" encoding="UTF-8"?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值