当前位置: 首页 > 知识库问答 >
问题:

根据数据库中的用户记录通过事件侦听器锁定帐户

吴哲
2023-03-14

我的问题类似于如何在 Spring 安全性中限制登录尝试?
问题是在遵循最近高票答案之后 https://stackoverflow.com/a/35294221/1278112

以下代码是我的方法。

@Component
public class AuthenticationEventListener {

    @Autowired
    private UserDetailsManager userDetailsManager;

    private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationEventListener.class);

    Set<RequestLimitRule> rules = Collections.singleton(RequestLimitRule.of(10, TimeUnit.MINUTES, 3)); // 3 request per 10 minute, per key
    RequestRateLimiter limiter = new InMemorySlidingWindowRequestRateLimiter(rules);

    @EventListener
    public void authenticationSuccess(AuthenticationSuccessEvent event) {
        String username = (String) event.getAuthentication().getName();
        if (!limiter.overLimitWhenIncremented(username)) {
            limiter.resetLimit(username);
        }
    }

    @EventListener
    public void authenticationFailed(AuthenticationFailureBadCredentialsEvent event) {
        String username = (String) event.getAuthentication().getPrincipal();
        boolean reachLimit = limiter.overLimitWhenIncremented(username);

        if (reachLimit) {
            User user = (User) userDetailsManager.loadUserByUsername(username);
            //User is security.core.userdetails.User
            user = (User) User.builder().username(user.getUsername()).accountExpired(!user.isAccountNonExpired())
                    .accountLocked(true).disabled(!user.isEnabled()).password(user.getPassword())
                    .authorities(user.getAuthorities()).build();
            userDetailsManager.updateUser(user);
        }
    }
}

它会抛出 UserNameNotFoundException 而不是 LockedException,发现 InMemoryUserDetailsManager 默认@Autowired,假设它没有用户信息。

由于用户信息存储在数据库中,因此尝试使用 JdbcUserDetailsManager 替换 InMemoryUserDetailsManager,但它允许在故障转移限制器后尝试登录。
在检查了JdbcUserDetailsManager的源代码后,我发现其中的updateUser()不会跟踪AccountNonLock属性。

@Ron Winch on Spring Security的答案:按名称注销,锁定或禁用用户

您还需要知道如何锁定帐户。Spring 安全性提供了一种支持锁定帐户的机制。

您可以使用 UserDetailsManager 实现来更新 UserDetails,以返回 false 表示 isAccountNonLocked。然后,Spring Security的DaoAuthenticationProvider将利用preAuthenticationChecks来确保帐户不会被锁定。

如果您编写了自己的 UserDetailsService,则内置的 UserDetailsManager 实现将不起作用,因此您需要更新您的用户模型,以便自定义 UserDetailsService 创建一个 UserDetailsService,该 UserDetailsService 返回 false 表示 isAccountNonLocked。

因此,使用自定义用户详细信息管理器实现来更新用户详细信息。它将更改记录数据库。但同样,当故障转移限制器时,它什么也不做。

搜索后,Spring Security 中的答案:LockedException 被抛出而不是 BadCredentialsException,为什么?指

默认预身份验证检查:检查锁定...

到达 spring 安全参考后发现有一个 DaoAuthenticationProvider

我是否必须以某种方式配置才能使其与自定义用户详细信息管理器一起使用?
还是有其他更好的途径来解决它?
提前谢谢。

共有1个答案

柴亦
2023-03-14

@Ron 绞盘说得很对

您可以使用 UserDetailsManager 实现来更新 UserDetails,以返回 false 表示 isAccountNonLocked。然后,Spring Security的DaoAuthenticationProvider将利用preAuthenticationChecks来确保帐户不会被锁定。

但是,InMemoryUserDetailsManager和JdbcUserDetailsManager不适合我。

所以我实现了UserDetailsService而不是UserDetailsManager

如果您编写了自己的 UserDetailsService,则内置的 UserDetailsManager 实现将不起作用,因此您需要更新您的用户模型,以便自定义 UserDetailsService 创建一个 UserDetailsService,该 UserDetailsService 返回 false 表示 isAccountNonLocked。

并通过 AuthenticationManagerBuilder 将其注入 AuthenticationManager

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;

@Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService).passwordEncoder(NoOpPasswordEncoder.getInstance());

    ....
}

UserDetailsServiceImpl的相关部分.java

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    ....

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //the custom implemetation
    }
}
 类似资料:
  • 我有一个烧瓶API,我使用烧瓶SQLAlChemy来处理SQLite数据库。我有一个存储日志条目的表,我想将最大行数限制为n。因为插入也是使用原始SQL从烧瓶之外的另一个脚本进行的,所以我创建了一个触发器来检查行数,如果行数高于n,则删除最旧的行数: 此触发器按预期工作,但我正在努力使用烧瓶-sqlalChemy设置它。如何设置触发器/执行原始SQL使用烧瓶sqlalChemy?SQL只需要在db

  • 问题内容: 我想按用户ID显示数据库中的记录。这意味着工作人员必须输入工作人员ID和密码并提出新项目的请求,该请求将保存在数据库中。 该请求工作正常,但是在request.php页面之后,我想显示该工作人员在receive.php中订购的项目。我该怎么办?这是定义表的SQL: 这是receive.php的PHP代码: 我真的希望有人可以检查我的代码。提前致谢。 问题答案: 您可以通过设置会话来做到

  • 问题内容: 我需要连续监视数据库行以检查更改(更新)。如果其他来源进行了某些更改或更新,则应在我的应用程序上触发该事件(我正在使用WCF)。有什么办法可以连续监听数据库行中的更改吗? 我可能拥有更多事件来监视同一表中的不同行。在性能方面有什么问题。我正在使用C#Web服务监视SQL Server后端。 问题答案: 不久前,我有一个非常相似的要求,我使用CLR SP将数据推送到消息队列中来解决了这个

  • 问题内容: 在JavaScript中,删除使用bind()添加为事件侦听器的函数的最佳方法是什么? 例 我能想到的唯一方法是跟踪添加了bind的每个侦听器。 上面的示例使用此方法: 有没有更好的方法可以做到这一点? 问题答案: 尽管@machineghost所说的是正确的,但事件的添加和删除方式相同,但等式中缺少的部分是: 调用后会创建一个新的函数引用! 因此,要添加或删除它,请将引用分配给变量:

  • 我正在尝试构建一个使用数据库的应用程序,允许用户以管理员或员工身份登录 管理员几乎没有选项: 添加新员工, 列出所有员工和 为选定员工添加待办事项 受雇者 登录时显示待办事项 我遇到的第一个问题是: 我应该为所有记录(员工)只创建一个表吗 第二个问题是,我不知道如何为成功登录的用户显示项目列表。如何访问登录用户的数据?

  • 问题内容: 我有一个SQL Server 2008数据库和一个asp.net前端。 当用户当前正在编辑记录但不确定哪种是最好的方法时,我想实现一个锁定。 我的想法是为记录创建一列,并在用户拉出该记录时将其设置为true,这意味着所有其他用户都具有只读访问权限,直到第一个用户完成编辑为止。 但是,如果会话超时并且他/她从不保存/更新记录,该记录将保留为,表示其他人无法对其进行编辑,对吗? 如何实现某

  • 当我尝试通过运行Laravel 4的网站发送电子邮件时,我会收到这个异常: 预期响应代码为250,但收到代码“535”,消息“535-5.7.8用户名和密码不被接受。有关详细信息,请访问535 5.7.8http://support.google.com/mail/bin/answer.py?answer=14257wd7sm12843789wjc。36-gsmtp“ 这是我的邮件配置: 我第一次