使用java配置在单个应用程序中的多个身份验证机制 [英] multiple authentication mechanisms in a single app using java config

查看:111
本文介绍了使用java配置在单个应用程序中的多个身份验证机制的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Currently I have a single authentication mechanism in my application which is to use LDAP for authentication and authorization. My security configuration looks like this

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

有些情况下用户可以使用会话令牌进入,该会话令牌可以从会话密钥服务器进行身份验证,并且有效令牌返回用户名,然后可以使用该用户名从LDAP为该用户加载身份验证信息。所以我的第二个身份验证机制应该首先发生,如果http头中存在会话令牌,它应该执行令牌身份验证然后执行ldap查找,如果没有会话令牌,它应该落入当前的身份验证机制。如何添加第二层身份验证。

There are situations though where users might come in with a session token which can authentication from a session key server and valid token returns a username which can then be used to load authrization information from LDAP for that user. So my second authentication mechanism should happen first where if a session token is present in http headers it should perform the token authentication and then ldap lookup and if no session token is present it should just fall to current authentication mechanism. How can I add this second layer of authentication.

推荐答案

我使用纯净的时候花了很长时间围绕弹簧安全java配置。让这个工作有一些步骤。它应该是这些方面的东西。基本流程如下:

I spent quite some time wrapping my head around spring-security when using pure java configuration. There are a few steps involved in getting this to work. It should be something along these lines. The basic process is as follows:


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

  • Create custom filters to check requests for specific authorization information

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

Each filter returns null (if no authorization of that type is found), or a custom AbstractAuthenticationToken

如果过滤器返回一个令牌,每个AuthenticationProvider的支持(类)方法将被调用,该令牌返回true | false如果它应该尝试身份验证

If a filter returns a token, each AuthenticationProvider's supports(class) method will be invoked with that token returning true|false if it should try authentication

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

attemptAuthentication will then be called on the AuthenticationProvider which supports the token. Here you do any service calls to authenticate the user. You can then throw LoginException's or call authentication.setAuthenticated(true) and return the token for a successful authentication.

I已经使用此设置一段时间支持各种身份验证方法(签名请求,用户名/密码,oauth等),它运作良好。

I have been using this setup for a while supporting various authentication methods (signed request, username/password, oauth etc) and it works quite well.

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

You can also pass AuthenticationSuccessHandler's and AuthenticationFailuersHandler's to the custom security filters to provide custom redirection strategies and failure handling.

还要确保在过滤器的构造函数中设置蚂蚁匹配器,以控制过滤器应用的URL模式。例如,ldap请求过滤器可能需要检查任何请求/ *,而用户名/密码过滤器可以在POST上检查到/ login或类似的东西。

Also be sure to setup the ant matchers in the filter's constructors to control what url patterns the filters apply too. For example, an ldap request filter would probably need to be check with any request "/*" whereas a username/password filter can just be checked on POST's to /login or something similar.

示例代码:

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

1) Create custom AuthenticationToken's for each type of authentication you want to support

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)为每种类型创建自定义安全过滤器

2) Create custom security filters for each type

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

3) Create custom 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();
    }
}

希望有所帮助!

这篇关于使用java配置在单个应用程序中的多个身份验证机制的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆