Spring Security 如何防止暴力破解?

在构建安全的 Web 应用时,防御暴力破解攻击是一个关键的考虑因素。Spring Security 提供了一套灵活的机制来帮助我们实现这一目标。本文将介绍如何使用 Spring Security 来记录失败的登录尝试,并在尝试次数超过一定阈值时封锁攻击者的 IP 地址。

亦可基于本文的思路实现封锁攻击者的账号。

一 封装登录计数器

@Service
public class LoginAttemptService {

    @Autowired
    RedisTemplate redisTemplate;

    public static final int MAX_ATTEMPT = 10;

    @Autowired
    HttpServletRequest request;

    public void loginFailed() {
        Integer attempts = null;
        attempts = (Integer) redisTemplate.opsForValue().get(getClientIP());
        if (attempts == null) {
            attempts = 0;
        }
        attempts++;
        redisTemplate.opsForValue().set(getClientIP(), attempts);
        redisTemplate.expire(getClientIP(), 1, TimeUnit.DAYS);
    }

    public boolean isBlocked() {
        Integer attempts = (Integer) redisTemplate.opsForValue().get(getClientIP());
        return attempts != null && attempts >= MAX_ATTEMPT;
    }

    private String getClientIP() {
        final String xfHeader = request.getHeader("X-Forwarded-For");
        if (xfHeader != null) {
            return xfHeader.split(",")[0];
        }
        return request.getRemoteAddr();
    }

    public void loginSuccess() {
        redisTemplate.delete(getClientIP());
    }
}

这里核心方法就三个:

  1. loginFailed:登录失败的时候调用该方法,以当前登录 IP 为 key,在 Redis 上保存登录失败的次数,并设置过期时间为 1 天。
  2. isBlocked:判断当前登录 IP 是否已经达到最大重试次数,达到了就要禁用该 IP 地址了(禁止时间为 1 天)。
  3. loginSuccess:登录成功后,清除该 IP 地址登录失败的记录。

二 监听器

前面 LoginAttemptService 中的方法,我们分别在登录成功和登录失败的监听器中进行调用。

首先,我们将创建一个 AuthenticationFailureListener,用于监听认证失败事件,并记录相关 IP 地址的失败尝试次数。

@Component
public class AuthenticationFailureListener implements
        ApplicationListener<AuthenticationFailureBadCredentialsEvent
{

    @Autowired
    private LoginAttemptService loginAttemptService;

    @Override
    public void onApplicationEvent(AuthenticationFailureBadCredentialsEvent e) {
        loginAttemptService.loginFailed();
    }
}

接下来再创建一个登录成功的监听器,用来清除当前 IP 登录失败的次数:

@Component
public class AuthenticationSuccessListener implements
        ApplicationListener<AuthenticationSuccessEvent
{

    @Autowired
    private LoginAttemptService loginAttemptService;

    @Override
    public void onApplicationEvent(AuthenticationSuccessEvent event) {
        loginAttemptService.loginSuccess();
    }
}

Spring Security 在登录成功或者失败的时候都会自动发布相关事件,我们这里只需要定义监听器就可以获取到事件,相关源码在 ProviderManager#authenticate 方法中,如下:

三 修改登录逻辑

在自定义的 UserDetailsService 实现中,我们需要在加载用户详情之前检查请求的 IP 地址是否已被封锁。

@Service
public class UserService implements UserDetailsService {

    @Autowired
    LoginAttemptService loginAttemptService;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        if (loginAttemptService.isBlocked()) {
            throw new RuntimeException("blocked");
        }
        return User.builder().username(username).password("{noop}123").authorities("admin").build();
    }
}

四 自定义认证失败处理器

最后,我们将修改 CustomAuthenticationFailureHandler,以自定义错误消息并通知用户其 IP 已被封锁。

@Component
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
    @Autowired
    private MessageSource messages;
    @Autowired
    private HttpServletRequest request;
    @Autowired
    private LoginAttemptService loginAttemptService;

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        // ... 错误处理逻辑
    }
}

五 结论

通过上述步骤,我们已经在 Spring Security 中实现了一个基本的暴力破解防御机制。不过在实际开发中,我们还可以采用更复杂的策略,如结合验证码、多因素认证等。

如果小伙伴们想要深入全面的学习 Spring Security,可以看看我最近录的最新版的 Spring Security+OAuth2 视频教程,这是视频目录:

这套视频教程基本上把 Spring Security 的方方面面以及 OAuth2 都讲到了。最重要的是,贴合了很多小伙伴们日常常见的开发场景,比如短信验证码登录,微信 OAuth2 登录等等都有讲到。

这套视频是基于目前最新版的 Spring Security6 录制的,Spring Security6 在 API 层面的变化还是蛮大的,引入了大量的 Lambda 表达式去简化配置,很多旧版的写法在 Spring Security6 中被废弃,并在将来在 Spring Security7 中会移除相关的 API,所以说最近的 Spring Security 更新还是蛮激进的。

另一方面就是这套视频包含了全新的基于目前最新版 Spring Security 录制的 OAuth2 教程,松哥在 2020 年的时候出过图文版的 OAuth2 教程,但是,现在新版的 OAuth2 也有很多变化,不仅仅是 API 层面的变化,授权模式也发生了一些变化,传统的密码模式、简化模式现在都不再支持,转而引入了 PKCE 模式,并且利用 OIDC 简化用户信息获取。同时,在 2020 年还处于萌芽状态的 Spring Security OAuth2 Server 这个项目,目前也趋于成熟,也可以直接使用了,这些松哥都在视频中和大家做了详细介绍。在 OAuth2 环节我也和大家分享了如何使用微信的 OAuth2 登录。

总之这一套教程,让大家彻底理解系统的安全管理。

之前有小伙伴说我直接自己写过滤器,既灵活还简单。我并不反对这种做法,但是有一个前提就是你很牛,你自己写的过滤器有考虑到计时攻击,有考虑到 XSS 攻击,有考虑到点击劫持,有考虑跨站请求伪造。。。等等太多了。当然,这些问题如果你都没有考虑到,那么 Spring Security 都有帮你考虑到并提供解决方案!

理解了 Spring Security,再去看市面上其他的安全管理框架,都会豁然开朗。

这套教程目前给小伙伴们提供了两个试看视频:

这套视频是付费的,¥499,有需要的小伙伴加微信备注 499 上车。


相关推荐

  • 面试官:BIO、NIO、AIO 的区别是什么?
  • 女同事35岁,五一节后再没露面,听说是被裁拿了10万,今天看到她退了群,但领导又反悔了,让她把10万补偿退回来。
  • [开源]一款多租户Saas快速开发平台,完全免费给个人及企业使用
  • 实操教程|称霸Kaggle的十大深度学习技巧
  • 早逝录:南林教师宋凯博士,因首聘期考核未过自杀身亡,享年38岁!
  • 可以一直做的低成本蓝海项目,适合新人练手
  • 每日 prompt:推荐这个风格画漫画
  • 谷歌也出大招了;字节推豆包大模型全家桶;Ilya Sutskever宣布退出OpenAI
  • OpenAI首席科学家Ilya离职,一个让马斯克与佩奇决裂的男人
  • 谷歌7大模型22项AI大招轰炸:70秒视频生成、Gemini安卓合体、200万tokens上下文
  • 腾讯混元文生图大模型全面开源!Sora同架构,更懂中文,免费商用
  • 字节豆包大模型发布!“比行业价格低99%”,对话火山引擎总裁谭待
  • 华为余承东履新后首次登台,Pura70“重新发布”,十七款重磅新品炸场
  • 打工人效率暴增神器!AI时代硬核办公图鉴,TWS耳机成全能会议助理
  • 全新的分布式锁,几行代码搞定,简单且强大
  • 32 个 Python 实例彻底解析 f-String 格式化浮点数 004【推荐收藏】
  • 京东秒送售后系统退款业务重构心得
  • 【腾讯云 BI 数据分析可视化大赛】有奖征文活动
  • 今天,创业4周年。
  • 混元文生图大模型正式开源:15 亿参数、首个中文原生DiT架构、与腾讯现网版本完全一致!