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

查看:17
本文介绍了使用 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 配置时,我花了相当多的时间来思考 spring-security.要使其发挥作用,需要执行几个步骤.它应该是沿着这些路线的.基本流程如下:

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

attemptAuthentication 然后将在支持令牌的 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.

我已经使用此设置一段时间了,支持各种身份验证方法(签名请求、用户名/密码、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) 配置 spring 安全

4) Configure spring security

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天全站免登陆