7

Avrei una domanda riguardante la configurazione di spring-security-oauth2 2.0.7. Sto facendo l'autenticazione tramite LDAP tramite un GlobalAuthenticationConfigurerAdapter:spring-security-oauth2 2.0.7 token di aggiornamento UserDetailsService Configuration - UserDetailsService è richiesto

@SpringBootApplication 
@Controller 
@SessionAttributes("authorizationRequest") 
public class AuthorizationServer extends WebMvcConfigurerAdapter { 

    public static void main(String[] args) { 
     SpringApplication.run(AuthorizationServer.class, args); 
    } 

    @Override 
    public void addViewControllers(ViewControllerRegistry registry) { 
     registry.addViewController("/login").setViewName("login"); 
     registry.addViewController("/oauth/confirm_access").setViewName("authorize"); 
    } 

    @Configuration 
    public static class JwtConfiguration { 

     @Bean 
     public JwtAccessTokenConverter jwtAccessTokenConverter() { 
      JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); 
      KeyPair keyPair = new KeyStoreKeyFactory(
        new ClassPathResource("keystore.jks"), "foobar".toCharArray()) 
        .getKeyPair("test"); 
      converter.setKeyPair(keyPair); 
      return converter; 
     } 

     @Bean 
     public JwtTokenStore jwtTokenStore(){ 
      return new JwtTokenStore(jwtAccessTokenConverter()); 
     } 
    } 


    @Configuration 
    @EnableAuthorizationServer 
    public static class OAuth2Config extends AuthorizationServerConfigurerAdapter implements EnvironmentAware { 

     private static final String ENV_OAUTH = "authentication.oauth."; 
     private static final String PROP_CLIENTID = "clientid"; 
     private static final String PROP_SECRET = "secret"; 
     private static final String PROP_TOKEN_VALIDITY_SECONDS = "tokenValidityInSeconds"; 

     private RelaxedPropertyResolver propertyResolver; 

     @Inject 
     private AuthenticationManager authenticationManager; 

     @Inject 
     private JwtAccessTokenConverter jwtAccessTokenConverter; 

     @Inject 
     private JwtTokenStore jwtTokenStore; 

     @Inject 
     private UserDetailsService userDetailsService; 

     @Override 
     public void setEnvironment(Environment environment) { 
      this.propertyResolver = new RelaxedPropertyResolver(environment, ENV_OAUTH); 
     } 

     @Bean 
     @Primary 
     public DefaultTokenServices tokenServices() { 
      DefaultTokenServices tokenServices = new DefaultTokenServices(); 
      tokenServices.setSupportRefreshToken(true); 
      tokenServices.setTokenStore(jwtTokenStore); 
      tokenServices.setAuthenticationManager(authenticationManager); 
      return tokenServices; 
     } 


     @Override 
     public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { 
      endpoints.authenticationManager(authenticationManager).tokenStore(jwtTokenStore).accessTokenConverter(
        jwtAccessTokenConverter).userDetailsService(userDetailsService); 
     } 

     @Override 
     public void configure(AuthorizationServerSecurityConfigurer oauthServer) 
       throws Exception { 
      oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess(
        "isAuthenticated()"); 
     } 

     @Override 
     public void configure(ClientDetailsServiceConfigurer clients) throws Exception { 
      clients.inMemory() 
        .withClient(propertyResolver.getProperty(PROP_CLIENTID)) 
        .scopes("read", "write") 
        .authorities(AuthoritiesConstants.ADMIN, AuthoritiesConstants.USER) 
        .authorizedGrantTypes("authorization_code", "refresh_token", "password") 
        .secret(propertyResolver.getProperty(PROP_SECRET)) 
        .accessTokenValiditySeconds(propertyResolver.getProperty(PROP_TOKEN_VALIDITY_SECONDS, Integer.class, 1800)); 
     } 
    } 

    @Configuration 
    @Order(-10) 
    protected static class WebSecurityConfig extends WebSecurityConfigurerAdapter { 

     @Override 
     protected void configure(HttpSecurity http) throws Exception { 
      http 
        .formLogin().loginPage("/login").permitAll() 
        .and() 
        .requestMatchers().antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access") 
        .and() 
        .authorizeRequests().anyRequest().authenticated(); 
     } 

     @Bean 
     @Override 
     public AuthenticationManager authenticationManagerBean() throws Exception { 
      return super.authenticationManagerBean(); 
     } 

     @Bean 
     @Override 
     public UserDetailsService userDetailsServiceBean() throws Exception { 
      return super.userDetailsServiceBean(); 
     } 
    } 

    @Configuration 
    protected static class AuthenticationConfiguration extends 
      GlobalAuthenticationConfigurerAdapter { 

     @Override 
     public void init(AuthenticationManagerBuilder auth) throws Exception { 
      auth 
        .ldapAuthentication() 
        .userDnPatterns("uid={0},ou=people") 
        .groupSearchBase("ou=groups") 
        .contextSource().ldif("classpath:test-server.ldif"); 
     } 
    } 
} 

Mentre il token di aggiornamento funziona bene con il rilascio 2.0.6 della primavera-sicurezza-OAuth2, esso non funziona più con la versione 2.0.7. Come letto here, si dovrebbe impostare AuthenticationManager da utilizzare quando si tenta di ottenere un nuovo token di accesso durante l'aggiornamento.

Per quanto ho capito, questo ha qualcosa a che fare con il cambio following di spring-security-oauth2.

Purtroppo non sono riuscito a configurarlo correttamente.

org.springframework.security.oauth2.provider.token.DefaultTokenServices#setAuthenticationManager 

viene chiamato e ottiene un AuthenticationManager iniettato. Non sono sicuro di aver capito come verrà iniettato lo LdapUserDetailsService. L'unica cosa che vedo è che il PreAuthenticatedAuthenticationProvider verrà chiamato durante il tentativo di autenticare nuovamente l'utente durante la chiamata di aggiornamento del token.

Qualcuno può consigliarmi su come farlo per favore?

ps: L'eccezione che sto ottenendo è la seguente:

p.PreAuthenticatedAuthenticationProvider : PreAuthenticated authentication request: org.springframework.security[email protected]5775: Principal: org.springframew[email protected]441d5545: Principal: bob; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_USER 
o.s.s.o.provider.endpoint.TokenEndpoint : Handling error: IllegalStateException, UserDetailsService is required. 
+1

Come descritto [qui] (https://github.com/spring-projects/spring-security-oauth/blob/e303652a911cf0168b5be68383ac120c23ec63b3/docs/oauth2 .md), 'userDetailsService: se si inietta un UserDetailsService o se uno è configurato globalmente in ogni caso (ad esempio in un GlobalAuthenticationManagerConfigurer), una concessione di token di aggiornamento conterrà un controllo sui dettagli dell'utente, per garantire che l'account sia ancora attivo. I mi sarei aspettato che il 'LdapUserDetailsService' venisse usato automaticamente. –

+0

Così vorrei (e funziona per me). Puoi pubblicare un progetto completo? –

+0

@ dave-syer, grazie mille per la risposta. Ho isolato il problema nel seguente progetto di test: https://github.com/jhoelter/zaas/tree/master/authserver c'è un tag chiamato ** spring-security-oauth2-2.0.6 ** dove funziona il token di aggiornamento bene. Quando si passa a 2.0.7 e si imposta la configurazione sopra descritta, si blocca. Molte grazie in anticipo per l'aiuto –

risposta

3

Come consigliato da Dave Syer, ho creato un numero personalizzato LdapUserDetailsService. La soluzione di lavoro è disponibile al seguente tag.

contesto applicativo

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> 

    <context:annotation-config/> 
    <context:property-placeholder location="application.yml"/> 

    <bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource"> 
     <constructor-arg value="${authentication.ldap.url}" /> 
    </bean> 

    <bean id="userSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch"> 
     <constructor-arg index="0" value="${authentication.ldap.userSearchBase}" /> 
     <constructor-arg index="1" value="uid={0}" /> 
     <constructor-arg index="2" ref="contextSource"/> 
    </bean> 

    <bean id="ldapAuthoritiesPopulator" class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator"> 
     <constructor-arg index="0" ref="contextSource"/> 
     <constructor-arg index="1" value="${authentication.ldap.groupSearchBase}"/> 
     <property name="groupSearchFilter" value="${authentication.ldap.groupSearchFilter}"/> 
    </bean> 

    <bean id="myUserDetailsService" 
      class="org.springframework.security.ldap.userdetails.LdapUserDetailsService"> 
     <constructor-arg index="0" ref="userSearch"/> 
     <constructor-arg index="1" ref="ldapAuthoritiesPopulator"/> 
    </bean> 

</beans> 

Proprietà

authentication: 
ldap: 
    url: ldap://127.0.0.1:33389/dc=springframework,dc=org 
    userSearchBase: 
    userDnPatterns: uid={0},ou=people 
    groupSearchBase: ou=groups 
    groupSearchFilter: (uniqueMember={0}) 
3

Quello che vi serve per il pezzo OAuth è quello di creare un LdapUserDetailsService con la stessa query come si Authenticator e inietta nel AuthorizationServerEndpointsConfigurer. Non credo che ci sia alcun supporto per la creazione di uno stile @Configuration nello stile @Configuration (potrebbe valere la pena aprire un biglietto per questo in JIRA), ma sembra che tu possa farlo in XML.

+0

Ciao Dave, grazie mille per il tuo feedback. Ho configurato un 'LdapUserDetailsService' personalizzato (usando un ApplicationContext XML vecchio stile). L'implementazione può essere trovata sotto questo [tag] (https://github.com/jhoelter/zaas/releases/tag/custom-LdapUserDetailsService). –

+0

Vedrò di creare un biglietto per questo. Trovo abbastanza pesante di dover creare un secondo 'LdapUserDetailsService' anche se uno è stato configurato automaticamente durante la chiamata' org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder # ldapAuthentication' –

+0

Jira [creato] (https://jira.spring.io/browse/SEC-3009) –

3

ho avuto un problema simile quando mi è stato l'implementazione di un server OAuth2 con JWT gettoni con un costume AuthenticationProvider invece di un'implementazione UserDetailsService per risolvere le autenticazioni di accesso.

Ma ultimamente ho trovato che l'errore Spring raise è corretto se si desidera che il refresh_token funzioni correttamente. Per un'implementazione AuthenticationProvider è impossibile aggiornare un token con un refresh_token, perché in quel tipo di implementazione è necessario risolvere se la password è corretta, ma il token di aggiornamento non ha quell'informazione. Tuttavia, UserDetailsService è agnostico della password.

La versione 2.0.6 di spring-security-oauth2 funziona perché non controlla mai le autorizzazioni dell'utente, controlla solo se il token di aggiornamento è valido (firmato con la chiave privata), ma, se l'utente è stato cancellato dal sistema dopo un primo accesso, con un token di aggiornamento l'utente eliminato avrà un accesso infinito al tuo sistema, che è un grosso problema di sicurezza.

Date un'occhiata al problema ho riferito con questo: https://github.com/spring-projects/spring-security-oauth/issues/813

Problemi correlati