【问题标题】:Use custom exceptions in Spring Security在 Spring Security 中使用自定义异常
【发布时间】:2012-08-15 15:13:06
【问题描述】:

我创建了一个自定义AuthenticationProvider 来执行自定义安全检查。我还创建了继承自AccountStatusException 的自定义异常,以通知用户状态问题,例如当用户在特定时间段内未验证其帐户时。我的UserDetails 也是自定义实现。

这是我执行的安全检查的代码。与案例无关的代码已被省略。

public class SsoAuthenticationProvider implements AuthenticationProvider {

    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = (String) authentication.getPrincipal();
        User user = null;
        if (username != null) {
            user = getUserRepository().findByUserName(username);
            if (user != null) {
                if (user.getEnabled() != 0) {
                    if ((user.getUserDetail().getConfirmed() != 0)
                            || ((new Date().getTime() - user.getUserDetail().getRequestDate().getTime()) / (1000 * 60 * 60 * 24)) <= getUnconfirmedDays()) {
                        if (getPasswordEncoder().isPasswordValid(user.getPassword(),
                                (String) authentication.getCredentials(), user)) {
                            user.authenticated = true;
                            user.getAuthorities();
                        }
                    } else {
                        throw new UserNotConfirmedAndTimeExceeded(
                                "User has not been cofirmed in the established time period");
                    }
                } else {
                    throw new DisabledException("User is disabled");
                }
            } else {
                throw new BadCredentialsException("User or password incorrect");
            }
        } else {
            throw new AuthenticationCredentialsNotFoundException("No credentials found in context");
        }
        return user;
    }
}

SsoAuthenticationProvider 检查:

  1. 用户名已注册(数据库中存在)
  2. 用户已确认他的电子邮件
  3. 如果用户尚未确认他的电子邮件,请检查他是否仍处于宽限期(这是我们给用户确认电子邮件的几天,同时让他们访问网站)
  4. 如果用户尚未确认电子邮件并且他不在宽限期内,则抛出安全异常以指示这些状态并拒绝身份验证

问题在于,并非所有这些异常都会在堆栈中向上抛出到控制器,因此似乎无法通知用户有关登录问题。

使用UserDetailsisEnabled()(和类似的)方法是不可能的,因为我们不同用户帐户状态的语义完全不同。

这是使用自定义异常构建自定义安全性的正确方法吗?我应该实施其他方法来完成这项工作吗?

【问题讨论】:

  • 这行得通,但我绝对不喜欢 if-else 链:D

标签: java spring-security


【解决方案1】:

为了结束之前提出的问题,让我解释一下我们做了什么。 正如我对之前的回复所评论的那样,在 UserDetails 对象中使用提供的方法是不可行的,因为您无法使用给定的方法捕获所有登录失败语义。在我们的例子中,这些语义仍然非常有限,但在其他情况下,它可以随着时间的推移无限延伸以表达不同的用户情况。 异常方法最终是最好的方法。最终的代码是这样的

    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    String username=(String)authentication.getPrincipal();
    User user=null;
    if(username!=null){
        user=getUserRepository().findByUserName(username);
        if(user!=null){
            if(user.getEnabled()!=0){
                if((user.getUserDetail().getConfirmed()!=0)||((new Date().getTime()-user.getUserDetail().getRequestDate().getTime())/(1000 * 60 * 60 * 24))<=getUnconfirmedDays()){
                    if(getPasswordEncoder().isPasswordValid(user.getPassword(), (String)authentication.getCredentials(), user)){
                        user.authenticated=true;
                        user.getAuthorities();
                    } else {
                        throw new BadCredentialsException("Password incorrect");
                    }
                }else{
                    throw new UserNotConfirmedAndTimeExceeded("User has not been cofirmed in the established time period");         
                }
            }else{
                throw new DisabledException("User is disabled");
            }
        }else{
            throw new BadCredentialsException("User does not exist");
        }
    }else{
        throw new AuthenticationCredentialsNotFoundException("No credentials found in context");
    }
    return user;
}

所有异常都是 Spring Security 异常堆栈的一部分。也就是说,那些自定义异常继承自一些现有异常。然后,在您的安全控制器中,您应该检查安全异常并根据需要处理它们。例如重定向到不同的页面。

希望这会有所帮助!

【讨论】:

    【解决方案2】:

    我认为最好使用user detail 对象的其他方法/属性来实现此目的。 喜欢

    isAccountNonExpired() 
    isAccountNonLocked() 
    isEnabled() 
    

    如果您想显示自定义错误消息,请使用 article 中解释的消息属性

    【讨论】:

    • 我认为您的建议不适用,因为我们尝试传达的帐户状态具有完全不同的语义。该帐户未过期,未锁定,已启用,但我们不再让用户登录,直到他确认他的电子邮件。
    • 也许你可以使用 authentication-success-handler-ref 来完成这项工作
    猜你喜欢
    • 2020-05-26
    • 2018-04-28
    • 2013-06-30
    • 1970-01-01
    • 2020-09-11
    • 1970-01-01
    • 2017-01-24
    • 2021-03-19
    • 2016-03-25
    相关资源
    最近更新 更多