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

ProviderManager。针对BadCredentialsException调用了两次身份验证

花博厚
2023-03-14

Spring 4.1和Spring Security 3.2:我们实现了一个自定义身份验证提供程序,如果用户输入了错误的密码,它会抛出一个Bad凭据异常。当抛出Bad凭据异常时,将调用ProviderManager.authenticate方法,该方法将再次调用自定义身份验证中的身份验证方法。当抛出LockedException时,不会再次调用自定义身份验证提供程序中的Authenate方法。我们计划保留登录尝试次数的计数,因此我们不希望身份验证方法被调用两次。有人知道为什么自定义身份验证类中的身份验证方法会被调用两次吗?

WebConfig:


  @Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomAuthenticationProvider customAuthenticationProvider;

    @Autowired
    private AMCiUserDetailsService userDetailsService;

    @Autowired
    private CustomImpersonateFailureHandler impersonateFailureHandler;

    @Autowired
    private LoginFailureHandler loginFailureHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
                .antMatchers("/jsp/*.css","/jsp/*.js","/images/**").permitAll()  
                .antMatchers("/login/impersonate*").access("hasRole('ADMIN') or hasRole('ROLE_PREVIOUS_ADMINISTRATOR')") 
                .anyRequest().authenticated()                                    
                .and()
            .formLogin()
                .loginPage("/login.jsp")
                .defaultSuccessUrl("/jsp/Home.jsp",true)                
                .loginProcessingUrl("/login.jsp")                                 
                .failureHandler(loginFailureHandler)
                .permitAll()
                .and()
            .logout()
                .logoutSuccessUrl("/login.jsp?msg=1")
                .permitAll()
                .and()
            .addFilter(switchUserFilter())
            .authenticationProvider(customAuthenticationProvider);

            http.exceptionHandling().accessDeniedPage("/jsp/SecurityViolation.jsp");  //if user not authorized to a page, automatically forward them to this page.
            http.headers().addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN)); 
    }


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(customAuthenticationProvider);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    //Used for the impersonate functionality
    @Bean CustomSwitchUserFilter switchUserFilter() {
        CustomSwitchUserFilter filter = new CustomSwitchUserFilter();
        filter.setUserDetailsService(userDetailsService);
        filter.setTargetUrl("/jsp/Impersonate.jsp?msg=0");
        filter.setSwitchUserUrl("/login/impersonate");
        filter.setExitUserUrl("/logout/impersonate");
        filter.setFailureHandler(impersonateFailureHandler);
        return filter;
    }
}

自定义身份验证提供程序:

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {

    @Autowired(required = true)
    private HttpServletRequest request;

    @Autowired
    private AMCiUserDetailsService userService;

    @Autowired
    private PasswordEncoder encoder;

    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        String username = authentication.getName().trim();
        String password = ((String) authentication.getCredentials()).trim();
        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
            throw new BadCredentialsException("Login failed! Please try again.");
        }


        UserDetails user;
        try {
            user = userService.loadUserByUsername(username);
            //log successful attempt
            auditLoginBean.setComment("Login Successful");
            auditLoginBean.insert(); 
        } catch (Exception e) {
             try {
                //log unsuccessful attempt
                auditLoginBean.setComment("Login Unsuccessful");
                auditLoginBean.insert();
             } catch (Exception e1) {
                // TODO Auto-generated catch block
             }
            throw new BadCredentialsException("Please enter a valid username and password.");
        }

        if (!encoder.matches(password, user.getPassword().trim())) {
            throw new BadCredentialsException("Please enter a valid username and password.");
        }

        if (!user.isEnabled()) {
            throw new DisabledException("Please enter a valid username and password.");
        }

        if (!user.isAccountNonLocked()) {
            throw new LockedException("Account locked. ");
        }

        Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
        List<GrantedAuthority> permlist = new ArrayList<GrantedAuthority>(authorities);

        return new UsernamePasswordAuthenticationToken(user, password, permlist);
    }


    public boolean supports(Class<? extends Object> authentication) {
        return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
    }

共有2个答案

亢琦
2023-03-14

可能有一种情况,您没有覆盖configure(AuthenticationManagerBuilder),但仍然相同的AuthenticationProverAuthentication方法会被调用两次,就像菲尔在接受的答案中的评论中提到的那样。

那是为什么呢?

原因是,当您不重写configure(AuthenticationManagerBuilder)并拥有AuthenticationProviderbean时,它将由Spring Security注册,您不必做任何其他事情。

但是,当重写配置(AuthenticationManagerBuilder)时,Spring Security会调用它,并且不会尝试自行注册任何提供程序。如果您好奇,可以查看相关方法。disableLocalConfigreAuthentiationBldr如果您重写confiure(AuthentiationManagerBuilder)则为真。

因此,简单地说,如果您只想注册一个自定义AuthenticationProvider,那么请不要覆盖配置(AuthenticationManagerBuilder),不要调用身份验证提供者(AuthenticationProvider)配置(HttpSecurity)中,只需通过注释@Component使您的AuthentiationProviver实现bean,您就可以开始了。

傅自明
2023-03-14

原因是您添加了两次身份验证提供程序,一次在configure(HttpSecurity)中,一次在configure(AuthenticationManagerBuilder)中。这将创建一个带有两项的ProviderManager,这两项都是您的提供者。

在处理身份验证时,将按顺序询问提供者,直到成功,除非抛出锁定异常或类似的状态异常,否则循环将中断。

 类似资料:
  • 在邮递员的每一个请求中,Vertx处理程序都会打两次电话。 我有两个处理程序,在调用请求处理处理程序之前将被调用。一个是设置头,一个是验证用户,但两个都是调用两次。 我在httpServerVerticle中的代码片段 在我得到的日志中

  • 我正在使用(来自命名空间)在应用程序中对Active Directory进行身份验证(我正在使用函数)。 身份验证工作正常,当我在Active Directory中重置用户密码并强制他在下一次登录时更改密码时,我无法对用户进行身份验证。 我尝试改变身份验证的方式,我使用了(命名空间),身份验证工作正常,当我重置用户密码并强制用户在下一次登录时更改密码时,我无法对该用户进行身份验证,如果我只是重置密

  • 问题内容: 我目前有一个Palm WebOS应用程序,该应用程序使用Ajax.Request通过基本身份验证连接到Web服务。要发送用户名和密码,我只需将其包含在url(即http:// username:password @ ip-address:port / )中即可,效果非常好,期望密码包含字母数字字符以外的任何字符(例如, ,最近有一个用户给我发送电子邮件,该用户的密码中包含“ @”和“&

  • 我正在开发一个带有两步身份验证的Symfony API应用程序。第一步是带有用户名和密码的表单,第二步是带有SMS服务的OTP。在json_login成功之后,json_login身份验证器必须创建jwt。因为,我使用令牌而不是PHP会话。它是通过选项在security.yaml文件中配置的。 利用Symfony框架可以制作多种不同的身份验证器和防火墙。在第一步(json_login)中,对用户进

  • 问题内容: 如何使用Python + LDAP针对AD进行身份验证。我目前正在使用python-ldap库,它所产生的只是眼泪。 我什至不能绑定执行简单的查询: 运行此命令会给我两个错误之一: -输入错误或故意使用错误的凭据时,它无法通过身份验证。 ldap.INVALID_CREDENTIALS:{‘info’:‘80090308:LdapErr:DSID-0C090334,评论:AcceptS

  • 我有一个Spring MVC应用程序(使用3.0.5版本),需要使用Spring LDAP绑定到Active Directory,以便简单且仅验证用户的凭据。该应用程序托管在Linux服务器上,因此我需要一个跨平台的解决方案。并且应用程序不使用Spring Security性。 在此设置中实现用户身份验证的有效方法是什么?Active Directory支持FastBind控件(id=