使用java配置在单个应用程序中的多个身份验证机制 [英] multiple authentication mechanisms in a single app using java config
问题描述
目前我的应用程序中有一个身份验证机制,即使用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屋!