2014-09-11 4 views
13

Attualmente nella mia applicazione ho un unico meccanismo di autenticazione che utilizza LDAP per l'autenticazione e l'autorizzazione. La mia configurazione di sicurezza simile a questameccanismi di autenticazione multipli in una singola app utilizzando java config

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

ci sono situazioni però in cui gli utenti possono entrare con un token di sessione che può autenticazione da un server di chiavi e Token validi sessione restituisce un nome utente che può quindi essere utilizzato per caricare authrization informazioni da LDAP per quell'utente. Quindi il mio secondo meccanismo di autenticazione dovrebbe accadere dapprima dove se un token di sessione è presente nelle intestazioni http dovrebbe eseguire l'autenticazione del token e quindi la ricerca di ldap e se nessun token di sessione è presente, dovrebbe semplicemente ricadere sul meccanismo di autenticazione corrente. Come posso aggiungere questo secondo livello di autenticazione.

risposta

26

Ho passato un po 'di tempo a concentrarmi sulla sicurezza di primavera quando si utilizzava la pura configurazione java. Ci sono alcuni passaggi necessari per farlo funzionare. Dovrebbe essere qualcosa in questo senso. Il processo di base è la seguente:

  • creare filtri personalizzati per verificare le richieste di informazioni specifica autorizzazione

  • Ogni filtro restituisce null (se non viene trovata alcuna autorizzazione di questo tipo), o un AbstractAuthenticationToken

  • personalizzato
  • Se un filtro restituisce un token, supporti di ogni AuthenticationProvider metodo (classe) saranno eseguiti con quella pedina ritorno true | false se si deve cercare di autenticazione

  • attemptAuthentication verrà quindi chiamato su AuthenticationProvider che supporta il token. Qui esegui tutte le chiamate di servizio per autenticare l'utente. È quindi possibile lanciare LoginException's o chiamare authentication.setAuthenticated (true) e restituire il token per un'autenticazione corretta.

Ho utilizzato questa configurazione per un po 'di supporto vari metodi di autenticazione (firmato richiesta, nome utente/password, OAuth, ecc) e funziona abbastanza bene.

È anche possibile passare AuthenticationSuccessHandler's e AuthenticationFailuersHandler ai filtri di sicurezza personalizzati per fornire strategie di reindirizzamento personalizzate e gestione degli errori.

Assicuratevi inoltre di impostare i formatori di formiche nei costruttori del filtro per controllare quali schemi di url si applicano anche ai filtri. Ad esempio, un filtro di richiesta ldap dovrebbe probabilmente essere controllato con qualsiasi richiesta "/ *" mentre un filtro username/password può essere controllato su POST a/login o qualcosa di simile.

Esempio di codice:

1) Creare personalizzati AuthenticationToken di per ogni tipo di autenticazione che si desidera supportare

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) Creazione di filtri di sicurezza personalizzati per ogni tipo

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) Crea autenticazione personalizzataProvider

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) Configurare la protezione primavera

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

Speranza che aiuta!

+0

Come funziona funziona? Che cos'è il tasto di intestazione "Autorizzazione"? Quali sono gli URL auth? Grazie – tayfun

8

Un'altra opzione per aggiungere un secondo provider di autenticazione: specificarne semplicemente un'altra su AuthenticationManagerBuilder. Poiché l'annotazione @EnableWebSecurity è annotata con EnableGlobalAuthentication, è possibile configurare l'istanza globale di AuthenticationManagerBuilder. (Vedi javadocs per ulteriori dettagli.)

Ad esempio, qui abbiamo un provider di autenticazione LDAP e un provider di autenticazione in memoria (codificato) (questo è qualcosa che facciamo in fase di sviluppo per far testare gli utenti locali con):

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

Questa sembra la risposta più semplice e corretta. –

0

Voglio solo aggiungere alla risposta di mclema. Potrebbe essere necessario aggiungere override per l'autenticazione di successo e continuare la catena dei filtri o altro utente viene reindirizzato a URL predefinito ("/") al posto di quello originale (es:/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); 
} 
Problemi correlati