java 在 Spring Security 中使用自定义异常

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12035133/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 07:21:59  来源:igfitidea点击:

Use custom exceptions in Spring Security

javaspring-security

提问by Daniel Cerecedo

I have created a custom AuthenticationProviderto perform custom security checks. I have also created custom exceptions that inherit from AccountStatusExceptionto notify user status problems such as when the user has not verified his account for an specific period of time.My UserDetailsis also acustom implementation.

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

Here is the code for the security checks I perform. Code that is irrelevant to the case has been omitted.

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

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;
    }
}

The SsoAuthenticationProviderchecks:

SsoAuthenticationProvider检查:

  1. That the username is registered (exists in the db)
  2. That the user has confirmed his email
  3. If the user has not confirmed his email, check that he is still in the grace period (this is a few days we give users to confirm their email while letting them access the site)
  4. If the user has not confirmed email and he is not in the grace period, throw security exception to signal these status and reject authentication
  1. 用户名已注册(存在于数据库中)
  2. 用户已确认他的电子邮件
  3. 如果用户没有确认他的电子邮件,请检查他是否仍处于宽限期(这是我们让用户在允许他们访问网站的同时确认他们的电子邮件的几天)
  4. 如果用户没有确认电子邮件并且他不在宽限期内,则抛出安全异常以表示这些状态并拒绝身份验证

The problem is that not all of these exceptions are thrown up the stack up to the controller so it seems impossible to inform the user about the login problem.

问题是并非所有这些异常都被抛出到控制器的堆栈中,因此似乎不可能通知用户登录问题。

Using UserDetailsmethods such as isEnabled()(and similar) is not a possibility as the semantics of our different user account statuses are completely different.

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

Is this the right approach to build custom security with custom exceptions? Should i implement sth else to make this work?

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

采纳答案by Daniel Cerecedo

To close the previously asked question let me explain what we did. As I commented to previous responses, using provided methods in UserDetails objectis not feasible as you cannot capture all the login failure semantics with the given methods. In our case these semantics are still very limited but in other cases it could indfinitely extend over time to express different user situations. The exception approach was finally the best one. The final code looks like this

为了结束之前提出的问题,让我解释一下我们做了什么。正如我对之前的回复所评论的那样,在 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;
}

All exceptions are part of the spring security exception stack. This is, those custom exceptions inherit from some existing exception. Then, in your security controller you should check for security exceptions and treat them as desired. For example redirecting to different pages.

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

Hope this helps!

希望这可以帮助!

回答by Jigar Parekh

i think its better to use other method/properties of user detailobject for this purpose. like

我认为最好为此目的使用用户详细信息对象的其他方法/属性。喜欢

isAccountNonExpired() 
isAccountNonLocked() 
isEnabled() 

and if you want to display custom error message then use message properties as explained in this article

如果你想显示自定义错误信息,然后使用邮件属性,因为这在解释文章