【Spring Security系列】5 次密码错误触发账号锁定?Spring Security 高效实现方案详解
作者:后端小肥肠
🍇 我写过的文章中的相关代码放到了gitee,地址:xfc-fdw-cloud: 公共解决方案
🍊 有疑问可私信或评论区联系我。
🥑 创作不易未经允许严禁转载。
姊妹篇:
【Spring Security系列】10分钟实现 SpringSecurity + CAS 完美单点登录方案_spring-security-cas-CSDN博客
【Spring Security系列】如何用Spring Security集成手机验证码登录?五分钟搞定!_springsecurity短信验证码登录-CSDN博客
【Spring Security系列】基于Spring Security实现权限动态分配之菜单-角色分配及动态鉴权实践_spring secrity权限角色动态管理-CSDN博客
【Spring Security系列】基于Spring Security实现权限动态分配之用户-角色分配_spring security 角色-CSDN博客
【Spring Security系列】权限之旅:SpringSecurity小程序登录深度探索_spring security 微信小程序登录-CSDN博客
【Spring Security系列】Spring Security+JWT+Redis实现用户认证登录及登出_spring security jwt 退出登录-CSDN博客
目录
1. 前言
2. 技术流程详解
2.1. 技术流程讲解
2.2. 涉及到的SpringSecurity核心组件
3. 技术实现
3.1. 表结构设计
3.2. 核心代码
4. 结语
1. 前言
在现代互联网应用中,账号安全是重中之重。然而,暴力破解攻击依然是最常见的安全威胁之一。攻击者通过自动化脚本尝试大量的用户名和密码组合,试图找到漏洞进入系统。尽管我们可以通过复杂密码要求、验证码、双因子认证等方式来提高安全性,但这些方法无法完全杜绝暴力破解的风险。
为了解决这一问题,账号锁定机制被广泛应用。通过限制用户的密码输入错误次数,当达到一定次数时锁定账号,可以有效防范暴力破解攻击,提高系统安全性。
以下是引入账号锁定机制的几大原因:
- 防止暴力破解攻击:攻击者短时间内多次尝试密码时,锁定机制能阻断其进一步操作。
- 保护用户资产和隐私:避免账号被盗造成的财产损失和隐私泄露。
- 提升系统安全性:通过限制无效登录尝试次数,降低因密码弱点引发的安全隐患。
- 应对安全合规要求:许多行业标准(如 GDPR、ISO27001)建议对异常登录行为采取保护措施。
在本文中,我们将通过 Spring Security 来实现一个简单而高效的账号锁定机制。当用户密码连续输入错误达到 5 次后,系统将自动锁定该账号3分钟。接下来,我们将通过技术流程、表结构设计、核心代码实现以及效果测试,详细讲解如何在 Spring Security 中落地这一功能。
2. 技术流程详解
2.1. 技术流程讲解
基于SpringSecurity实现密码错误锁定账号的流程如下:
其实光看流程图已经能很清晰地理解完整流程,我这边还是用文字再给大家梳理一遍:
1. 用户提交登录请求
用户输入用户名和密码后发起登录请求,系统开始验证其身份。
2. 加载用户信息
Spring Security 使用自定义的 UserService
加载用户信息,包括用户名、密码、锁定状态(accountNonLocked
)、登录失败次数(loginAttempts
)、锁定时间(lockTime
)等。
3. 检查账号锁定状态
- 如果账号被锁定:
- 判断锁定时间是否已过:
- 锁定时间未过:抛出
LockedException
,阻止登录。 - 锁定时间已过:解锁账号(
accountNonLocked = true
)、重置登录失败次数(loginAttempts = 0
),并允许继续登录。
- 锁定时间未过:抛出
- 判断锁定时间是否已过:
- 如果账号未锁定,直接进入密码验证。
4. 验证密码
- 密码正确:
- 登录成功,重置用户状态(失败次数清零,锁定状态解除),并更新到数据库。
- 密码错误:
- 增加登录失败次数(
loginAttempts + 1
)。 - 如果失败次数达到 5 次或以上,锁定账号(
accountNonLocked = false
),并记录当前锁定时间(lockTime = 当前时间
)。
- 增加登录失败次数(
5. 更新用户状态
- 登录成功时:更新用户信息到数据库,并执行登录成功后的业务逻辑。
- 登录失败时:更新用户的失败次数和锁定状态到数据库,并执行失败后的处理逻辑。
6. 等待下次登录尝试
用户根据账号状态和锁定时间,决定何时再次发起登录请求。
2.2. 涉及到的SpringSecurity核心组件
以上流程涉及到的SpringSecurity核心组件如下:
1. AuthenticationManager
- 管理认证流程,调用
AuthenticationProvider
验证用户凭据。
2. AuthenticationProvider
- 执行具体的认证逻辑,包括密码验证和账号状态检查。
3. PasswordEncoder
- 用于密码加密和验证,例如使用
BCryptPasswordEncoder
。
4. AuthenticationSuccessHandler
- 处理登录成功后的逻辑,例如重置失败登录次数和解锁账号。
5. AuthenticationFailureHandler
- 处理登录失败后的逻辑,例如增加登录失败次数和锁定账号。
6. AuthenticationEntryPoint
- 处理未认证用户访问受保护资源时的逻辑,返回错误信息。
7. SecurityContextHolder
- 存储和提供当前用户的认证信息(
Authentication
对象)。
8. ExceptionTranslationFilter
- 捕获认证过程中抛出的异常,并交给
AuthenticationEntryPoint
或AuthenticationFailureHandler
处理。
9. UsernamePasswordAuthenticationFilter
- 处理基于用户名和密码的登录请求,触发认证流程。
10.HttpSecurity
- 配置认证流程和组件,包括认证、授权和异常处理。
这些组件共同协作,实现 Spring Security 的认证和密码错误锁定账号功能。
3. 技术实现
3.1. 表结构设计
密码错误账号锁定只涉及到user表,这是我的表结构,你可以根据你自己的灵活调整:
CREATE TABLE user (id BIGINT PRIMARY KEY AUTO_INCREMENT,username VARCHAR(50) UNIQUE NOT NULL,password VARCHAR(255) NOT NULL,login_attempts INT DEFAULT 0, -- 登录失败次数lock_time TIMESTAMP NULL, -- 账号锁定时间account_non_locked BOOLEAN DEFAULT TRUE -- 账号是否锁定
);
3.2. 核心代码
1. 编写自定义UsernamePasswordAuthenticationFilter
public class SecurUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {@AutowiredISysUserService userService;@Overridepublic Authentication attemptAuthentication(HttpServletRequest request,HttpServletResponse response) throws AuthenticationException {if (request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE)|| request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)) {ObjectMapper mapper = new ObjectMapper();UsernamePasswordAuthenticationToken authRequest = null;//取authenticationBeanMap<String, String> authenticationBean = null;//用try with resource,方便自动释放资源try (InputStream is = request.getInputStream()) {authenticationBean = mapper.readValue(is, Map.class);} catch (IOException e) {//将异常放到自定义的异常类中throw new SecurAuthenticationException(e.getMessage());}try {if (!authenticationBean.isEmpty()) {//获得账号、密码String username = authenticationBean.get(SPRING_SECURITY_FORM_USERNAME_KEY);String password = authenticationBean.get(SPRING_SECURITY_FORM_PASSWORD_KEY);request.setAttribute(SPRING_SECURITY_FORM_USERNAME_KEY, username);//检测账号、密码是否存在if (userService.checkLogin(username, password)) {//将账号、密码装入UsernamePasswordAuthenticationToken中authRequest = new UsernamePasswordAuthenticationToken(username, password);setDetails(request,authRequest );return this.getAuthenticationManager().authenticate(authRequest);}}} catch (Exception e) {throw new SecurAuthenticationException(e.getMessage());}return null;} else {return this.attemptAuthentication(request, response);}}
}
这段代码是一个自定义的 Spring Security 登录过滤器,用于处理 JSON 格式的登录请求(代替默认的表单登录)。它通过解析请求体中的 JSON,提取用户名和密码,并调用用户服务检查登录信息是否正确。如果验证通过,则创建并返回一个 UsernamePasswordAuthenticationToken
,交由认证管理器执行进一步的认证流程;否则,抛出自定义异常终止认证。
2. 编写userService.checkLogin方法
public boolean checkLogin(String username, String rawPassword) throws Exception {SysUser userEntity = this.getUserByUserName(username);System.out.println("userEntity = " + userEntity);if (userEntity == null) {//System.out.println("checkLogin--------->账号不存在,请重新尝试!");//设置友好提示throw new Exception("账号不存在,请重新尝试!");} else {// 检查账号锁定状态handleAccountLock(userEntity);//加密的密码String encodedPassword = userEntity.getPassword();//和加密后的密码进行比配if (!passwordEncoder.matches(rawPassword, encodedPassword)) {//System.out.println("checkLogin--------->密码不正确!");//设置友好提示throw new Exception("密码不正确!");} else {return true;}}}
这段代码实现了用户登录验证逻辑,首先通过用户名获取用户信息,如果用户不存在则抛出异常提示账号不存在。接着检查用户账号的锁定状态(调用 handleAccountLock
方法),如果账号未锁定,则验证用户输入的原始密码是否与加密存储的密码匹配。密码匹配成功则返回 true
,否则抛出异常提示密码不正确。
3. 编写 handleAccountLock方法
/*** 检查并处理账号锁定逻辑*/private void handleAccountLock(SysUser user) {if (!user.getAccountNonLocked() && user.getLockTime() != null) {// 当前时间Date now = new Date();// 解锁时间Date unlockTime = new Date(user.getLockTime().getTime() + LOCK_DURATION.toMillis());if (now.before(unlockTime)) {throw new LockedException("账号已锁定,请3分钟后再试");}// 解锁账号并重置状态user.setAccountNonLocked(true);user.setLoginAttempts(0);user.setLockTime(null);this.updateById(user);}}
这段代码实现了账号锁定状态的检查与处理逻辑。如果用户账号已锁定且存在锁定时间,系统会计算解锁时间。如果当前时间在解锁时间之前,则抛出异常提示账号被锁定;如果超过了解锁时间,则解锁账号,同时重置失败次数和锁定时间,并更新用户信息到数据库。
4. 编写increaseFailedAttempts,用于用户登录失败时调用
public void increaseFailedAttempts(SysUser user) {int attempts = user.getLoginAttempts() + 1;if (attempts >= MAX_LOGIN_ATTEMPTS) {user.setAccountNonLocked(false);user.setLockTime(new Date());}user.setLoginAttempts(attempts);this.updateById(user);}
这段代码实现了增加用户登录失败次数的逻辑。当用户登录失败时,登录失败次数 (loginAttempts
) 增加 1;如果失败次数达到或超过最大允许次数 (MAX_LOGIN_ATTEMPTS
),系统会将用户账号设置为锁定状态 (accountNonLocked=false
) 并记录锁定时间 (lockTime
)。最后,将更新后的用户状态保存到数据库。
5. 编写resetLoginAttempts,用于用户登录成功时调用
public void resetLoginAttempts(String username) {SysUser user = this.getUserByUserName(username);if(user==null){throw new UsernameNotFoundException(String.format("%s.这个用户不存在或已被禁用", username));}user.setLoginAttempts(0);user.setAccountNonLocked(true);user.setLockTime(null);this.updateById(user);}
这段代码实现了重置用户登录失败次数的逻辑。通过用户名查询用户信息,如果用户不存在则抛出异常。若用户存在,则将其登录失败次数 (loginAttempts
) 重置为 0,解锁账号 (accountNonLocked=true
),并清除锁定时间 (lockTime=null
)。最后,将更新后的用户信息保存到数据库。
6. 编写自定义AuthenticationSuccessHandler,在用户登录成功时调用resetLoginAttempts
7. 编写自定义AuthenticationFailureHandler
@Component
public class SecurAuthenticationFailureHandler extends JSONAuthentication implements AuthenticationFailureHandler {@AutowiredSysUserServiceImpl sysUserService;@Overridepublic void onAuthenticationFailure(HttpServletRequest request,HttpServletResponse response,AuthenticationException e) throws IOException, ServletException {// 从请求体中解析 JSON,获取 usernameString username = (String) request.getAttribute(SPRING_SECURITY_FORM_USERNAME_KEY);// 登录失败时增加失败次数if (username != null) {SysUser user = sysUserService.getUserByUserName(username);if (user != null) {sysUserService.increaseFailedAttempts(user);}}ResponseStructure data = ResponseStructure.instance(ALL_RETURN_401.getCode(),"登录失败:"+e.getMessage());//输出this.WriteJSON(request, response, data);}
}
这段代码是一个自定义的登录失败处理器 SecurAuthenticationFailureHandler
,在用户登录失败时被触发。它通过解析请求获取登录失败的用户名,并调用服务方法 increaseFailedAttempts
增加用户的登录失败次数。同时,根据异常信息生成统一的响应结构,并以 JSON 格式返回错误信息给客户端,提示登录失败的原因。
4. 结语
本文详细讲解了如何通过 Spring Security 实现密码错误多次后自动锁定账号的功能。从需求分析到技术流程,再到核心代码实现,我们完整梳理了账号锁定机制的设计与落地方法。
账号锁定机制是提升系统安全性的重要手段,能够有效防范暴力破解攻击。在实际应用中,还可以结合具体需求进行优化,例如添加解锁通知或动态调整锁定策略等。
希望本文对您有所帮助,若有疑问或建议,欢迎交流!
相关文章:

【Spring Security系列】5 次密码错误触发账号锁定?Spring Security 高效实现方案详解
作者:后端小肥肠 🍇 我写过的文章中的相关代码放到了gitee,地址:xfc-fdw-cloud: 公共解决方案 🍊 有疑问可私信或评论区联系我。 🥑 创作不易未经允许严禁转载。 姊妹篇: 【Spring Security系列…...

笔记day5
文章目录 1 复习2 最完美的解决方案,解决轮播图问题3 开发Floor组件4 把首页中的轮播图拆分为一个共用全局组件5 search模块开发 1 复习 完成商品分类三级列表路由跳转一级路由传参(合并参数)完成search模块中对于typeNav的使用(…...

Linux快速入门-兼期末快速复习使用
Linux快速入门-兼期末快速复习使用 一小时快速入门linux快速一:Linux操作系统概述1. Linux概述1.1 定义与特点1.2 起源与发展1.3 Linux结构1.4 版本类别1.5 应用和发展方向 2. 安装与启动2.1 Windows下VMware安装Linux2.2 安装Ubuntu 快速二:linux的桌面…...

浅谈文生图Stable Diffusion(SD)相关模型基础
1.U-Net模型基础 1.基础概念 UNet模型是一种基于卷积神经网络的图像分割算法,它采用了U型的网络结构,由编码器(下采样路径)和解码器(上采样路径)两部分组成。 编码器负责提取输入图像的特征,…...

Vivado使用VScode编译器
旧版Vivado使用Vscode编译器偶尔会出现VScode界面卡死的情况,在新版的Vivado中(我的是Vivado 2023.2),可以使用如下方式: 在设置中选择Text Editor,选择Custom Editor 在对话框中输入以下语句:…...

CEF127 编译指南 MacOS 篇 - 拉取 CEF 源码(五)
1. 引言 在完成了所有必要工具的安装和配置后,我们进入到获取 CEF 源码的阶段。对于 macOS 平台,CEF 的源码获取过程需要特别注意不同芯片架构(Intel 和 Apple Silicon)的区别以及版本管理。本文将详细介绍如何在 macOS 系统上获…...

Jenkins 中 写 shell 命令执行失败,检测失败问题
由于项目的 依赖复杂,随着版本的增多,人工操作,手误几率太大,我们选取kenins 来自动化发布、更新。 这里主要解决,发布 的 每个阶段,确保每个阶段执行成功。 比如: js 运行,…...

Java程序打包成exe,无Java环境也能运行
Java程序开发完成后,通常情况下以jar包的形式发布。但有时我们需要给非软件开发人员使用程序,如制作好窗体应用,把它发给没有java开发环境的人使用,此时就需要制作exe安装包。本文介绍如何将java程序制作成exe安装包,并…...

【java 正则表达式 笔记】
文章目录 快速入门匹配中文或数字或大小写字母(一个或多个) 正则表达式底层实现(重要)mather.find() 完成的任务mather.group(0) 分析 正则表达式基本语法元字符转义字符区分大小写限定字符选择匹配符特殊字符字符匹配符定位符 分组、捕获和反向引用捕获特别分组反向引用经典结…...

基于PWLCM混沌映射的麋鹿群优化算法(Elk herd optimizer,EHO)的多无人机协同路径规划,MATLAB代码
一、麋鹿群优化算法EHO 基本概念 麋鹿群优化算法(EHO,Elephant Herding Optimization)是2024年提出的一种启发式优化算法,它的灵感来自麋鹿群的繁殖过程。麋鹿有两个主要的繁殖季节:发情和产犊。在发情季节࿰…...

Vue2五、自定义指令,全局局部注册、指令的值 ,插槽--默认插槽,具名插槽 ( 作用域插槽)
一、自定义指令 使用步骤 1. 注册 (全局注册 或 局部注册) ,在 inserted 钩子函数中,配置指令dom逻辑 2. 标签上 v-指令名 使用 1、自定义指令(全局) Vue.directive("指令名",{ 指令的配置项 insert…...

Pika Labs技术浅析(五):商业智能技术
Pika Labs 的商业智能旨在通过联机分析处理(OLAP)和数据仓库(Data Warehouse)等技术,帮助企业用户高效地进行数据分析和决策支持。 一、商业智能技术模块概述 Pika Labs 的商业智能技术模块旨在通过集成数据仓库和联机…...

YOLO-World:Real-Time Open-Vocabulary Object Detection
目录 摘要 Abstract YOLO-World 1 模型架构 1.1 Text Encoder 1.2 YOLO Backbone 2 RepVL-PAN 2.1 T-CSPLayer 2.2 I-Pooling Attention 2.3 预测 3 消融实验 3.1 预训练数据 3.2 RepVL-PAN的消融实验 3.3 文本编码器 4 效果展示 4.1 零样本 4.2 根据词汇表检…...

Fastjson <= 1.2.47 反序列化漏洞复现
0x01 前言 Fastjson 是一个 Java 语言编写的高性能功能完善的 JSON 库,可以将 Java 对象转换为 JSON 格式,也可以将 JSON 字符串转换为 Java 对象,在中国和美国使用较为广泛。 0x02 漏洞成因 Fastjson < 1.2.68 版本在处理反序列化对象时…...

鸿蒙项目云捐助第二十一讲云捐助项目物联网IoT模拟器的使用
鸿蒙项目云捐助第二十一讲云捐助项目物联网IoT模拟器的使用 在前面的内容中,已经实现了云捐助物联网IoT的产品及设备设置,并且使用华为云Iot设备的在线调试工具进行命令下发的调试,这里也可以通过华为Iot物联网提供的MQTT模拟器进行连接。 …...

大数据技术原理与应用期末复习-知识点(二)
HBASE Hbase与传统关系数据库的对比分析 1.数据类型: 关系型数据库采用关系模型 Hbase采用更简单的数据模型(把数据存储为未经解释的字符串) 2.数据操作: 关系数据库:增删改查等 Hbase:插入 查询 删…...

高效准确的PDF解析工具,赋能企业非结构化数据治理
目录 准确性高:还原复杂版面元素 使用便捷:灵活适配场景 贴心服务:快速响应机制 在数据为王的时代浪潮中,企业数据治理已成为组织优化运营、提高竞争力的关键。随着数字化进程的加速,企业所积累的数据量呈爆炸式增长…...

C/C++ 数据结构与算法【栈和队列】 栈+队列详细解析【日常学习,考研必备】带图+详细代码
一、介绍 栈和队列是限定插入和删除只能在表的“端点”进行的线性表,是线性表的子集,是插入和删除位置受限的线性表。 (操作受限的线性表) 二、栈 1)概念: 栈(stack)是一个特殊的线性表,是限…...

读书笔记~管理修炼-缄默效应
缄默效应:学会正确批评下属 员工明明犯了错误,却不及时告知你,总是拖到最后一刻无法弥补时才不得不承认出了问题——你遇到过这样的问题吗? 这其实是缄默效应在发挥作用。 在职场中,即使再扁平化的环境&…...

视频会议系统会前预约模块必须包含哪些功能?
视频会议系统会前预约模块必须包含哪些功能? 视频会议系统的会前预约模块是企业高效管理会议资源、提升会议效率的重要工具。一个完善的会前预约模块必须包含一系列功能,以确保会议的顺利进行和资源的合理分配。以下是对视频会议系统会前预约模块必须包…...

RabbitMQ中的Topic模式
在现代分布式系统中,消息队列(Message Queue)是实现异步通信、解耦系统组件的重要工具。RabbitMQ 是一个广泛使用的开源消息代理,支持多种消息传递模式,其中 Topic 模式 是一种灵活且强大的模式,允许生产者…...

tslib(触摸屏输入设备的轻量级库)的学习、编译及测试记录
目录 tslib的简介tslib的源码和make及make install后得到的文件下载tslib的主要功能tslib的工作原理tslib的核心组成部分tslib的框架和核心函数分析tslib的框架tslib的核心函数ts_setup()的分析(对如何获取设备名和数据处理流程的分析)函数ts_setup()自身的主要代码ts_setup()对…...

Ubuntu vi(vim)编辑器配置一键补全main函数
1.打开对应的配置文件 vi ~/.vim/snippets/c.snippets 2.按G将光标定位到文件末尾 3.按i进入插入模式 以tab键开头插入下的内容,空行也要加 tab键 4.:wq保存退出 5.再打开任意一个新的 .c文件后,插入模式输入 main 然后按tal键就能补全了...

验证码机制
偶然间看到了验证码机制,顺便总结一下: 首先,验证码是从后端生成的,随机生成; 【后端永远认为前端有可能会被伪造】 1.后端调用相关的绘图第三方类库,或是(平台PHP、.NET、java)系…...

【CVE-2024-56145】PHP 漏洞导致 Craft CMS 出现 RCE
大多数开发人员都同意,与 15 年前相比,PHP 是一种更加理智、更加安全和可靠的语言。PHP5早期的不良设计已让位于更好的开发生态系统,其中包括类、自动加载、更严格的类型、更理智的语法以及一大堆其他改进。安全性也没有被忽视。 register_globals一些老读者可能还记得和的…...

使用FakeSMTP创建本地SMTP服务器接收邮件具体实现。
以下代码来自Let’s Go further节选。具体说明均为作者本人理解。 编辑邮件模版 主要包含三个template: subject:主题plainBody: 纯文本正文htmlBody:超文本语言正文 {{define "subject"}}Welcome to Greenlight!{{end}} {{def…...

【网络安全】逆向工程 练习示例
1. 逆向工程简介 逆向工程 (RE) 是将某物分解以了解其功能的过程。在网络安全中,逆向工程用于分析应用程序(二进制文件)的运行方式。这可用于确定应用程序是否是恶意的或是否存在任何安全漏洞。 例如,网络安全分析师对攻击者分发…...

Oracle Database 21c Express Edition数据库 和 Sqlplus客户端安装配置
目录 一. 前置条件二. Win10安装配置Oracle数据库2.1 数据库获取2.2 数据库安装2.3 数据库配置确认2.4 数据库访问 三. Win10配置Oracle数据库可对外访问3.1 打开文件和打印机共享3.2 开放1521端口 四. 端口与地址确认4.1 查看监听器的状态4.2 Win10查看1521端口是否被监听4.3 …...

arcgisPro将面要素转成CAD多段线
1、说明:正常使用【导出为CAD】工具,则导出的是CAD三维多线段,无法进行编辑操作、读取面积等。这是因为要素面中包含Z值,导出则为三维多线段数据。需要利用【复制要素】工具禁用M值和Z值,再导出为CAD,则得到…...

相机内外参知识
已知相机的内外参数矩阵,可以求得相机在世界坐标系下的原点坐标。这里需要理解几个概念: 内参数矩阵(Intrinsic Matrix): 描述相机本身的属性,比如焦距、主点位置等。外参数矩阵(Extrinsic Matrix…...