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

当用户身份验证失败时,Spring Security ProviderNotFoundException

王才英
2023-03-14

我已经设置了一个LDAP自定义身份验证提供程序,类似于这里的示例-https://www.baeldung.com/spring-security-authentication-provider

有一个登录控制器来处理登录错误,并检查用户是否在已批准的列表中。控制器调用自定义身份验证提供程序,即authenticationManager。authenticate()方法。

如果提供了错误的凭据,将调用自定义身份验证提供程序两次。引发了两个异常。

第一个例外:

31-12-2020 15:42:55.577 [http-nio-9090-exec-6] ERROR c.c.t.a.CustomAuthenticationProvider.hasAccess - test is not authenticated
javax.naming.AuthenticationException: [LDAP: error code 49 - 80090308: LdapErr: DSID-0C09044E, comment: AcceptSecurityContext error, data 52e, v2580 ]
        at com.sun.jndi.ldap.LdapCtx.mapErrorCode(Unknown Source) ~[na:1.8.0_261]
        at com.sun.jndi.ldap.LdapCtx.processReturnCode(Unknown Source) ~[na:1.8.0_261]
        at com.sun.jndi.ldap.LdapCtx.processReturnCode(Unknown Source) ~[na:1.8.0_261]
        at com.sun.jndi.ldap.LdapCtx.connect(Unknown Source) ~[na:1.8.0_261]
        at com.sun.jndi.ldap.LdapCtx.ensureOpen(Unknown Source) ~[na:1.8.0_261]
        at com.sun.jndi.ldap.LdapCtx.ensureOpen(Unknown Source) ~[na:1.8.0_261]
        at com.sun.jndi.ldap.LdapCtx.reconnect(Unknown Source) ~[na:1.8.0_261]
        at javax.naming.ldap.InitialLdapContext.reconnect(Unknown Source) ~[na:1.8.0_261]
        at com.tools.auth.CustomAuthenticationProvider.hasAccess(CustomAuthenticationProvider.java:65) [classes!/:1.0.0-SNAPSHOT]
        at com.tools.auth.CustomAuthenticationProvider.authenticate(CustomAuthenticationProvider.java:32) [classes!/:1.0.0-SNAPSHOT]
        at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174) [spring-security-core-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
        at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:199) [spring-security-core-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
        at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:502) [spring-security-config-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
        at com.tools.web.JwtAuthenticationRestController.authenticate(JwtAuthenticationRestController.java:70) [classes!/:1.0.0-SNAPSHOT]

第二个例外:

c.c.t.a.CustomAuthenticationProvider.authenticate - User does not have access
31-12-2020 15:42:55.613 [http-nio-9090-exec-6] ERROR c.c.t.w.JwtAuthenticationRestController.authenticate - Exception logging in user
org.springframework.security.authentication.ProviderNotFoundException: No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken
        at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:227) ~[spring-security-core-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
        at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:502) ~[spring-security-config-5.0.3.RELEASE.jar!/:5.0.3.RELEASE]
        at com.tools.web.JwtAuthenticationRestController.authenticate(JwtAuthenticationRestController.java:70) [classes!/:1.0.0-SNAPSHOT]

这是自定义提供程序:

public class CustomAuthenticationProvider implements AuthenticationProvider {

    @Override
    public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {
        String name = authentication.getName();
        String password = authentication.getCredentials().toString();

        if (hasAccess(name, password)) {
            Authentication auth = new UsernamePasswordAuthenticationToken(name,
                    password);
            return auth;
        } else {
            return null;
        }
    }

    public boolean supports(Class<?> authentication) {
        return true;
    }

    public boolean hasAccess(final String username, final String password) {
    //LDAP access happens here
    }
}

这是控制器:

public class JwtAuthenticationRestController {

    @Autowired
    private AuthenticationManager authenticationManager;

    @CrossOrigin
    @RequestMapping(value = "/authenticate", method = RequestMethod.POST)
    public ResponseEntity<?> createAuthenticationToken(@RequestBody JwtTokenRequest authenticationRequest)
            throws AuthenticationException {
        authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());
         //generate token
        return ResponseEntity.ok(new JwtTokenResponse(token));
    }

    @ExceptionHandler({AuthenticationException.class})
    public ResponseEntity<String> handleAuthenticationException(AuthenticationException e) {
    //handle exception. set custom response.
    }

    private void authenticate(String username, String password) {   
        try {
            // Check against the approved user list
            //Authenticate the user - Exception thrown here
            authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
        }catch (Exception e) {
            throw new AuthenticationException("APPLICATION_ERROR", e);
        }
    }
}

此处更新的是Web安全配置:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class JWTWebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtUnAuthorizedResponseAuthenticationEntryPoint jwtUnAuthorizedResponseAuthenticationEntryPoint;

    @Autowired
    private JwtTokenAuthorizationOncePerRequestFilter jwtAuthenticationTokenFilter;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(new CustomAuthenticationProvider());
    }   

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
            .csrf().disable()
            .exceptionHandling().authenticationEntryPoint(jwtUnAuthorizedResponseAuthenticationEntryPoint).and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            .authorizeRequests()
            .anyRequest().authenticated();

        httpSecurity
                .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

        httpSecurity
            .headers()
            .frameOptions().sameOrigin()  
            .cacheControl(); 
    }

    @Override
    public void configure(WebSecurity webSecurity)  {
        webSecurity
            .ignoring()
            .antMatchers(
                HttpMethod.POST,
                "/authenticate" //authentication path
            )
            .antMatchers(HttpMethod.OPTIONS, "/**")
            .and()
            .ignoring()
            .antMatchers(
                HttpMethod.GET,
                        "\"login" //Ignore security for Login page.
                )
            .and()
            .ignoring()
            .antMatchers("/h2-console/**/**");    
    }

仅当身份验证因密码无效而失败时才会发生这种情况。我检查了自定义提供程序抛出javax.naming.AuthenticationException,并为valis凭据返回null。

为什么Spring会对失败的身份验证抛出此异常?解决方法是将控制器中的异常作为登录失败进行处理,但仍然需要了解为什么会发生这种情况。

共有2个答案

章誉
2023-03-14

仅从AuthenticationProvider实现并不能解决您的目的。您需要向AuthenticationManagerBuilder注册提供商。希望您没有错过注册身份验证提供程序的步骤


@Configuration
@EnableWebSecurity
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {

private final CustomAuthenticationProvider customAuthenticationProvider;

@Autowired
public MyWebSecurityConfig(CustomAuthenticationProvider customAuthenticationProvider){
 this.customAuthenticationProvider = customAuthenticationProvider;
}

@Override
public void configure(AuthenticationManagerBuilder authBuilder) throws Exception {
  authBuilder.authenticationProvider(CustomAuthenticationProvider );
}

在WebSecurity配置类中插入自定义提供,并将该字段设置为身份验证提供程序之一。

谈禄
2023-03-14

您是否验证了正在从控制器中调用CustomAuthenticationProvider?很明显,这个例外来自ProviderManager类。从ProviderManager的javadoc:

如果没有提供程序返回非null响应,或者指示它甚至可以处理身份验证,那么ProviderManager将抛出ProviderNotFoundException。

如果您已经学习了Baeldung示例,那么spring在调用堆栈中处理的身份验证过程要比您的控制器高得多。

 类似资料:
  • 我有一个API在Apache堆栈上运行,使用Java。我继承了一个遗留代码库,需要找到这个错误来自哪里显然是上一个开发人员构建了一个不能工作的特性,这就是错误所在,但没有更多信息。它使用Drupal webservice模块,发送调用的代码如下所示 我可以从哪里开始寻找这个错误,或者它可能意味着什么,你有什么想法吗? 完整的堆栈跟踪如下所示::: 用户失败:null。java.lang.Unsup

  • Tweepy API请求twitter return me Twitter错误响应:状态代码=401。 这是我的实际代码: 我曾试图用tweepy软件包删除推文,并获得了所有必需的密钥。镊子包装不起作用吗?有人能帮我解决这个问题吗。

  • 因此,我在Centos 8和postgresql-12上遇到了身份验证问题。添加服务器期间出现以下错误:::pgadmin4错误创建服务器 本地主机上的连接:postgres连接本地主机 我真的尝试了很多答案,但没有一个对我有用。pg_nba.conf文件现在有以下字符串: 本地所有信任 本地所有md5 托管所有127.0.0.1/32 md5 主机所有::1/128 md5 托管所有10.1.1

  • 我正在组装一个soap客户端来调用第三方soap服务。我在连接Java时遇到问题。它在SoapUI上运行良好。这是我第一次在应用程序中设置密钥库。我发现的所有代码都是相同的,非常简单,但我不明白为什么java版本不起作用。。我使用的是该公司提供的TLS pfx文件,我也在尝试连接该公司的服务。我要从服务器上拿回403。。这是密码 非常感谢任何对我错误的帮助... 不确定是否重要,但服务器是一个.

  • 问题内容: 尝试使用JavaMail中的NTLM连接到Exchange服务器。我可以连接到SMTP,但不能连接到IMAP。我还可以使用相同的主机/用户名/密码通过OS X Mail.app应用程序进行身份验证,帐户类型=“ IMAP”,端口143,ssl = false,authentication = NTLM,域名=“。 连接代码: 输出: 我尝试通过http://www.oracle.com

  • 我相对来说是JMeter的新手,但是我很难让HTTP Sampler登陆到一个安全的网页上。我认为它需要NTLM认证,所以我使用HTTP授权管理器来传递BlazeMeter指南中指定的凭证 我的授权管理器具有以下值: 基本网址: https:// [测试站点] 用户名: [我的用户名] 密码: [我的密码] 域:与基本网址相同 机制: BASIC_DIGEST 然而,我只是得到一个401错误(见下