当前位置: 首页 > 面试题库 >

使用Java配置在单个应用程序中使用多种身份验证机制

章哲彦
2023-03-14
问题内容

当前,我的应用程序中只有一种身份验证机制,即使用LDAP进行身份验证和授权。我的安全配置如下所示

@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .csrf().disable()
        .authorizeRequests()
            .anyRequest().fullyAuthenticated()
            .and()
            .httpBasic();
}

@Configuration
protected static class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter {

    @Value("${ldap-${env}.manager.dn}")
    private String managerDn;

    @Value("${ldap-${env}.manager.pass}")
    private String managerPass;

    @Value("${ldap-${env}.server.url}")
    private String url;

    @Value("${ldap.password.attribute:userPassword}")
    private String passwordAttr;

    @Override
    public void init(AuthenticationManagerBuilder auth) throws Exception {
        auth.ldapAuthentication().userDnPatterns("uid={0},ou=people").groupSearchBase("ou=groups")
                .groupSearchFilter("(member={0})").userSearchBase("ou=people").userSearchFilter("(uid={0})")
                .userDetailsContextMapper(new CustomLdapPersonContextMapper())
                // .passwordCompare()
                // .passwordAttribute(passwordAttr)
                // .passwordEncoder(new PlaintextPasswordEncoder())
                // .and()
                .contextSource().managerDn(managerDn).managerPassword(managerPass).url(url);
    }
}
}

但是,在某些情况下,用户可能会带有可以从会话密钥服务器进行身份验证的会话令牌,而有效令牌会html" target="_blank">返回用户名,然后该用户名可用于从LDAP加载该用户的身份验证信息。因此,我的第二种身份验证机制应该首先发生,如果HTTP标头中存在会话令牌,则应执行令牌身份验证,然后进行ldap查找,如果不存在会话令牌,则应采用当前身份验证机制。如何添加第二层身份验证。


问题答案:

使用纯Java配置时,我花了很多时间来围绕spring-security进行研究。要使其正常工作,涉及几个步骤。应该遵循这些原则。基本过程如下:

  • 创建自定义过滤器以检查请求以获取特定的授权信息

  • 每个过滤器返回null(如果未找到该类型的授权),或者返回自定义AbstractAuthenticationToken

  • 如果过滤器返回令牌,则每个AuthenticationProvider的support(class)方法将被调用,并且该令牌如果应尝试进行身份验证,则返回true | false

  • 然后将在支持令牌的AuthenticationProvider上调用tryAuthentication。在这里,您可以进行任何服务调用以验证用户身份。然后,您可以抛出LoginException或调用authentication.setAuthenticated(true)并返回用于成功认证的令牌。

我一直在使用此设置一段时间,以支持各种身份验证方法(签名请求,用户名/密码,oauth等),并且效果很好。

您还可以将AuthenticationSuccessHandler和AuthenticationFailuersHandler传递到自定义安全过滤器,以提供自定义重定向策略和故障处理。

另外,请务必在过滤器的构造函数中设置ant匹配器,以控制过滤器也应用哪种url模式。例如,ldap请求过滤器可能需要与任何请求“ /
*”一起检查,而用户名/密码过滤器仅可以在POST到/ login或类似的地方进行检查。

示例代码:

1)为您要支持的每种身份验证类型创建自定义AuthenticationToken

public class LDAPAuthorizationToken extends AbstractAuthenticationToken {
    private String token;

    public LDAPAuthorizationToken( String token ) {
        super( null );
        this.token = token;
    }

    public Object getCredentials() {
        return token;
    }

    public Object getPrincipal() {
        return null;
    }
}

public class OTPAuthorizationToken extends UsernamePasswordAuthenticationToken {
    private String otp;

    public OTPAuthorizationToken( String username, String password, String otp ) {
        super( username, password );
        this.otp = otp;
    }

    public String getOTP() {
        return otp;
    }
}

2)为每种类型创建自定义安全过滤器

public class LDAPAuthorizationFilter extends AbstractAuthenticationProcessingFilter {
    @Autowired
    private UserDetailsService userDetailsService;

    public LDAPAuthorizationFilter() {
        super( "/*" ); // allow any request to contain an authorization header
    }

    public Authentication attemptAuthentication( HttpServletRequest request, HttpServletResponse response ) throws AuthenticationException
    {

        if ( request.getHeader( "Authorization" ) == null ) {
            return null; // no header found, continue on to other security filters
        }

        // return a new authentication token to be processed by the authentication provider
        return new LDAPAuthorizationToken( request.getHeader( "Authorization" ) );
    }
}

public class OTPAuthorizationFilter extends AbstractAuthenticationProcessingFilter {
    @Autowired
    private UserDetailsService userDetailsService;

    public OTPAuthorizationFilter() {
        super( "/otp_login" );
    }

    public Authentication attemptAuthentication( HttpServletRequest request, HttpServletResponse response ) throws AuthenticationException
    {

        if ( request.getParameter( "username" ) == null || request.getParameter( "password" ) == null || request.getParameter( "otp" ) == null ) {
            return null;
        }

        // return a new authentication token to be processed by the authentication provider
        return new OTPAuthorizationToken( request.getParameter( "username" ), request.getParameter( "password" ), request.getParameter( "otp" ) );
    }
}

3)创建自定义AuthenticationProviders

public class LDAPAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private MyAuthenticationService sampleService;

    @Override
    public Authentication authenticate( Authentication authentication ) throws AuthenticationException {
        LDAPAuthorizationToken auth = (LDAPAuthorizationToken)authentication;

        String username = sampleService.verifyToken( auth.getCredentials() );
        if ( username == null ) {
            throw new LoginException( "Invalid Token" );
        }

        auth.setAuthenticated( true );

        return auth;
    }

    @Override
    public boolean supports( Class<?> authentication ) {
        if ( authentication.isAssignableFrom( LDAPAuthorizationToken.class ) ) {
            return true;
        }
        return false;
    }
}

public class OTPAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private MyAuthenticationService sampleService;

    @Override
    public Authentication authenticate( Authentication authentication ) throws AuthenticationException {
        OTPAuthorizationToken auth = (OTPAuthorizationToken)authentication;

        String error = sampleService.loginWithOTP( auth.getPrincipal(), auth.getCredentials(), auth.getOTP() );
        if ( error != null ) {
            throw new LoginException( error );
        }

        auth.setAuthenticated( true );

        return auth;
    }

    @Override
    public boolean supports( Class<?> authentication ) {
        if ( authentication.isAssignableFrom( OTPAuthorizationToken.class ) ) {
            return true;
        }
        return false;
    }
}

4)配置弹簧安全性

public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure( HttpSecurity http ) throws Exception {
        // configure filters
        http.addFilterBefore( new LDAPAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class );
        http.addFilterBefore( new OTPAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class );

        // configure authentication providers
        http.authenticationProvider( new LDAPAuthenticationProvider() );
        http.authenticationProvider( new OTPAuthenticationProvider() );

        // disable csrf
        http.csrf().disable();

        // setup security
        http.authorizeRequests()
            .anyRequest()
                .fullyAuthenticated()
                .and().httpBasic();
    }
}

希望有帮助!



 类似资料:
  • 但是,在某些情况下,用户可能会带着一个会话令牌进来,该令牌可以从会话密钥服务器进行身份验证,有效令牌返回一个用户名,然后可以使用该用户从LDAP加载Authridization信息。因此,我的第二个身份验证机制应该首先进行,如果会话令牌存在于http头中,则应该执行令牌身份验证,然后进行ldap查找,如果没有会话令牌,则应该使用当前的身份验证机制。如何添加第二层身份验证。

  • 问题内容: 我想在我的Java应用程序中使用Windows NTLM认证来透明地认证Intranet用户。如果使用浏览器(单点登录),用户将不会注意到任何身份验证。 我发现了一些具有NTLM支持的库,但是不知道要使用哪个库: http://spnego.sourceforge.net/ http://sourceforge.net/projects/ntlmv2auth/ http://jcifs

  • 我正在尝试配置一个具有多种身份验证机制(DB和LDAP)并使用Spring Security性作为其底层框架的应用程序。我正在使用java配置来设置Web和http安全性。我知道我们需要多个WebSecurityConfigurerAdapter实例来存储多个http元素(如基于xml的config中使用的);但是当我这样做时,应用程序只选择配置的第一个身份验证(数据库身份验证),并且从不使用第二

  • 我有一个需要以两种不同方式保护的API: 1) 对除1以外的所有请求URL使用JWT,该URL需要使用基本身份验证进行保护 2) 一个url的基本身份验证。 我已经为JWT和Basic Auth设置了安全配置。我的问题是,当我使用有效的用户名和密码请求基本的经过身份验证的URL时,它会成功地对我进行身份验证,并将数据存储在cassandra中。 然后,我希望必须通过/api/login为所有其他请

  • 问题内容: 在Spring Security中,有多个身份验证提供程序的参考,但是找不到Java config中的示例。 以下链接给出了XML表示法: Spring Security中的多个身份验证提供程序 我们需要使用LDAP或DB进行身份验证 下面是我们的示例代码: 问题答案: 也许这会帮助你:

  • 我正在使用Spring Boot构建一个REST Api,我可以使用Oauth2(使用spring-security-oauth2)来保护它。 我想为执行器管理endpoint(度量、健康等)管理一个单独的身份验证和授权模式。 并且我希望管理endpoint运行在不同的端口(application.properties中的management.port=8081)上 我读了很多书,但找不到一个方法