mécanismes d'authentification multiples dans une seule application utilisant java config
actuellement, j'ai un seul mécanisme d'authentification dans mon application qui est D'utiliser LDAP pour l'authentification et l'autorisation. Ma configuration de sécurité ressemble à ceci
@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);
}
}
}
Il y a cependant des situations où les utilisateurs peuvent venir avec un token de session qui peut s'authentifier à partir d'un serveur clé de session et un token valide renvoie un nom d'utilisateur qui peut ensuite être utilisé pour charger des informations d'authentification à partir de LDAP pour cet utilisateur. Donc, mon deuxième mécanisme d'authentification devrait se produit d'abord lorsque Si un token de session est présent dans les en-têtes http, il doit effectuer l'authentification du token, puis la recherche ldap et si aucun token de session n'est présent, il doit tomber dans le mécanisme d'authentification actuel. Comment puis-je ajouter cette deuxième couche d'authentification.
4 réponses
j'ai passé pas mal de temps à me concentrer sur la sécurité des sources en utilisant la configuration java pure. Il y a quelques étapes à franchir pour que cela fonctionne. Ce devrait être quelque chose dans ce sens. Le processus de base est la suivante:
créer des filtres personnalisés pour vérifier les demandes d'informations d'autorisation spécifiques
chaque filtre renvoie null (si aucune autorisation de ce type n'est trouvée), ou une AbstractAuthenticationToken
si un filtre renvoie un token, chaque méthode de support(classe) D'AuthenticationProvider sera invoquée avec ce token retournant true|false si elle doit essayer l'authentification
tentative d'authentification sera alors appelée Sur le fournisseur D'authentification qui supporte le token. Ici vous effectuez des appels de service pour authentifier l'utilisateur. Vous pouvez alors lancer LoginException ou l'authentification d'appel.setathenticated (true) et retourner le jeton pour une authentification réussie.
j'utilise cette configuration depuis un certain temps en supportant différentes méthodes d'authentification (demande signée, nom d'utilisateur/mot de passe, etc.) et cela fonctionne assez bien.
vous pouvez également passer les Authentifications Successhandler's et Authenticationfailuershandler's aux filtres de sécurité personnalisés pour fournir des stratégies de redirection personnalisées et la gestion des échecs.
assurez-vous également de mettre en place la fourmi matchers dans les constructeurs du filtre pour contrôler les motifs d'url que les filtres appliquent aussi. Par exemple, un filtre de requête ldap devrait probablement être vérifié avec n'importe quelle requête "/*" alors qu'un filtre de nom d'utilisateur/mot de passe peut simplement être vérifié sur POST's to /login ou quelque chose de similaire.
Exemple De Code:
1) Créez des authentifications personnalisées pour chaque type d'authentification que vous voulez prendre en charge
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) Créer des filtres de sécurité personnalisés pour chaque 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) Créer des fournisseurs d'authentification sur mesure
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) Configurer la sécurité du printemps
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();
}
}
j'Espère que ça aide!
autre, option pour ajouter un second fournisseur d'authentification: spécifiez simplement un autre sur le AuthenticationManagerBuilder
. Parce que @EnableWebSecurity
l'annotation est elle-même annotée de EnableGlobalAuthentication
vous pouvez configurer l'instance globale de AuthenticationManagerBuilder
. (Voir javadoc pour plus de détails.)
Par exemple, ici nous avons un fournisseur d'authentification LDAP ainsi qu'une mémoire (codé en dur) fournisseur d'authentification (c'est quelque chose que nous faisons dans le développement local aux utilisateurs de tester avec l'):
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${user.role}")
private String userRole; // i.e. ROLE_APP_USER
@Value("${include.test.users}")
private boolean includeTestUsers;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**/js/**").permitAll()
.antMatchers("/**/images/**").permitAll()
.antMatchers("/**/favicon.ico").permitAll()
.antMatchers("/**/css/**").permitAll()
.antMatchers("/**/fonts/**").permitAll()
.antMatchers("/**").hasAnyRole(userRole)
.and().formLogin().loginPage("/login").permitAll().and().logout().permitAll();
http.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth, LdapContextSource contextSource) throws Exception {
auth.ldapAuthentication()
.userSearchBase("OU=Users OU")
.userSearchFilter("sAMAccountName={0}")
.groupSearchBase("OU=Groups OU")
.groupSearchFilter("member={0}")
.contextSource(contextSource);
if (includeTestUsers) {
auth.inMemoryAuthentication().withUser("user").password("u").authorities(userRole);
}
}
}
je veux juste ajouter à mclema de réponse. Vous pouvez avoir besoin d'ajouter un correcteur pour l'authentification réussie et continuer la chaîne de filtrage ou bien l'utilisateur est redirigé vers l'url par défaut ("/") au lieu de l'url d'origine (par exemple: /myrest/server/somemethod)
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authResult);
SecurityContextHolder.setContext(context);
chain.doFilter(request, response);
}
La accepté de répondre a la question que la présente demande n'est pas accordée ie. seulement pour les demandes suivantes la session est établie! J'ai donc dû configurer au point 2
public class MyAuthorizationFilter extends AbstractAuthenticationProcessingFilter {
public MyAuthorizationFilter() {
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
}
// required to use the token
myNewToken = new MyAuthorizationToken( request.getHeader( "Authorization" ) );
// and set in the current context ==> the current request is as well authorized
SecurityContextHolder.getContext().setAuthentication(myNewToken);
// return a new authentication token to be processed by the authentication provider
return myNewToken;
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
// try to authenticate the current request
attemptAuthentication((HttpServletRequest) req, (HttpServletResponse) res);
super.doFilter(req, res, chain);
}
}
sinon, la requête actuelle n'est pas encore authentifiée bien qu'une session soit déjà créée!!! (Et les fournisseurs dont je n'ai pas besoin, c'est-à-dire ajouter un filtre est suffisant.)