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

多个身份验证提供程序:如果身份验证失败,请不要委派

周玺
2023-03-14

我正在尝试在Spring身份验证服务器(Spring Security)中配置多个身份验证提供程序(主要和次要)

根据“身份验证”方法留档:

返回:包含凭据的完全经过身份验证的对象。如果AuthenticationProvider无法支持对传递的身份验证对象的身份验证,则可能返回null。在这种情况下,将尝试下一个支持提供的身份验证类的AuthenticationProvider。

如果身份验证失败,则抛出:身份验证html" target="_blank">异常。

基于此,我在主提供程序上实现了authenticate方法,如下所示(我将省略SecondaryAuthProvider实现):

//PrimaryAuthProvider.class
public Authentication authenticate(Authentication authentication) {
    var user = authServices.getLdapUser(authentication.getName());

    //log and let the next provider handle it
    if (user == null) {
        logServices.userNotFound(new LogServices.AuthFailure(authentication.getName()));             
        return null;
    }

    if (passwordMatches(authentication.getCredentials(), user.getStringPassword())) {
        return authenticatedToken(user);
    } else {
        logServices.authFailure(new LogServices.AuthFailure(authentication.getName()));
        throw new BadCredentialsException("Invalid password");
    }
}

在WebSecurity中,我还注入了我的提供商:

protected void configure(AuthenticationManagerBuilder auth) {
    auth.authenticationProvider(primaryAuthProvider);
    auth.authenticationProvider(secondaryAuthProvider);
}

这将正确处理问题,如果:

  • 无论提供商如何,用户都被告知正确的登录名/密码。
  • 无论密码是否正确,都无法在主提供程序上找到用户。

如果在主提供程序上找到用户,并且其密码错误,则会抛出BadCredentialsException,但服务器仍将委托给辅助提供程序,并且最终的消息将是“user not found”,这具有误导性。

我以为BadCredentialsException会完成身份验证链并向客户机/用户报告,但事实似乎并非如此。

我错过了什么吗?

共有1个答案

满耀
2023-03-14

好吧,我想起来了。

Provider Manager是服务器上使用的默认身份验证管理器。如果发生AuthenticationException,它的身份验证方法实际上会委托给下一个提供者:

for (AuthenticationProvider provider : getProviders()) {
    if (!provider.supports(toTest)) {
        continue;
    }

    //(...)

    try {
        result = provider.authenticate(authentication);

        if (result != null) {
            copyDetails(authentication, result);
            break;
        }
    }
    catch (AccountStatusException | InternalAuthenticationServiceException e) {
        prepareException(e, authentication);
        // SEC-546: Avoid polling additional providers if auth failure is due to
        // invalid account status
        throw e;
    } catch (AuthenticationException e) {
        lastException = e;
    }
}

我发现了两种方法。

第一个:如果身份验证失败,请在主提供程序上提供未经身份验证的令牌,并且不要引发任何异常:

//PrimaryAuthProvider.class
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    var user = authServices.getLdapUser(authentication.getName());

    if (user == null) return null;

    if (passwordMatches(authentication.getCredentials(), user.getStringPassword())) {
        return authenticatedToken(user);
    } else {        
        return unauthenticatedToken(user);
    }
}

private UsernamePasswordAuthenticationToken unauthenticatedToken(LdapUser user)   {
        //Using 2 parameter constructor => authenticated = false 
        return new UsernamePasswordAuthenticationToken(
            user.getLogin(),
            user.getStringPassword(),
        );
}

这有一个用英语显示默认消息的缺点。我需要在其他地方拦截异常并用葡萄牙语抛出一个新的异常。

第二个(希望我使用):将我自己的AuthorizationManager实现为ProviderManager的一个较小版本。这一条不会试图捕捉提供商发起的异常:

public class CustomProviderManager implements AuthenticationManager {
    private final List<AuthenticationProvider> providers;

    public CustomProviderManager(AuthenticationProvider... providers) {
        this.providers = List.of(providers);
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        for (var provider : providers) {
            if (!provider.supports(authentication.getClass())) continue;

            //let exceptions go through
            var result = provider.authenticate(authentication); 
            if (result != null) {
                return result;
            }
        }

        throw new ProviderNotFoundException(
            "No provider for " + authentication.getName()
        );
    }
}

然后,在WebSecurityConfig:

@Bean(BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
    return authenticationManager();
}

@Override
protected AuthenticationManager authenticationManager() {
    return new CustomProviderManager(primaryAuthProvider, secondaryAuthProvider);
}


// Don't need it anymore
//    @Override
//    protected void configure(AuthenticationManagerBuilder auth) {
//        auth.authenticationProvider(authenticationProvider);
//        auth.authenticationProvider(secondaryAuthProvider);
//    }

第二个需要更多的编码,但给了我更多的控制权。

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

  • 但是这个配置的问题是我只能以用户Euclid的身份登录。但我想作为每一个可用的用户登录。 但是对于另一个配置,我会在日志中得到这样的消息,即找到了该用户,但已经发生了驱逐。 2017-04-20 09:36:16]ldap_driver.debug:ldap_search(dc=example,dc=com,(&(&(objectclass=person))(UID=einstein)),[arr

  • 我正在尝试为正在进行的 spring-boot 项目实现身份验证和授权服务。我已经实现了一个基于 JPA 的身份验证提供程序,它工作正常。如何将 LDAP 身份验证提供程序添加到同一项目并根据用户身份验证类型在身份验证方法之间切换? 下面是我的代码 虽然我的LDAP证书是正确的,但它没有达到那个方法。如何从DB ex(LDAP、JPA、SSO)获取我的应用程序的身份验证方法,并执行相应的身份验证提

  • 问题内容: 我的Web应用程序有多个身份验证管理器(一个用于API,一个用于WEB访问)。该api应该仅具有基本的身份验证服务- 通过spring安全标记进行配置,如下所示: 我无法内联身份验证提供程序,因为我希望它可以被子bean配置覆盖。 我的问题是我无法在security:authentication-provider元素上定义别名/ id以在身份验证管理器中引用它。有一个简单的解决方法吗?

  • 问题内容: 使用Passport.js是否可以为同一路由指定多个身份验证提供程序? 例如(在护照指南中),我可以在以下示例路线中使用本地策略以及Facebook和Twitter策略吗? 问题答案: Passport的中间件的构建方式使您可以在一个呼叫中使用多种策略。 但是,它是用OR顺序定义的。也就是说,只有在所有策略均未成功返回的情况下,它才会失败。 这是您将如何使用它: 换句话说,使用它的方法

  • 我的问题是,我希望有两个身份验证提供商 之前:我有我的UserDetailServiceImpl,我根据数据库中的数据验证了用户的身份(不确定是哪个提供者) 现在:我使用了ActiveDirectoryLdapAuthentiation提供程序,如下所示 我成功了,所以我可以认证。 问题是: 我现在无法再使用数据库用户登录,现在只有LDAP。 未使用UserDetailsService,因此用户具