Come decidere in modo dinamico il valore dell’attributo di accesso in Spring Security?

In Spring Security utilizziamo il tag intercept-url per definire l’accesso per gli URL come di seguito:

  

Questo è hard coded in applicationContext-security.xml . Voglio invece leggere i valori di accesso da una tabella di database. Ho definito il mio UserDetailsService e leggo i ruoli per l’utente che ha effettuato l’accesso dal database. Come posso assegnare questi ruoli ai pattern URL durante il runtime?

La class FilterInvocationSecurityMetadataSourceParser in Spring-security (prova Ctrl / Cmd + Shift + T in STS con il codice sorgente) analizza i tag intercept-url e crea le istanze di ExpressionBasedFilterInvocationSecurityMetadataSource, che estende DefaultFilterInvocationSecurityMetadataSource che implementa FilterInvocationSecurityMetadataSource che estende SecurityMetadataSource.

Quello che ho fatto è creare una class personalizzata che implementa FilterInvocationSecurityMetadataSource, OptionsFromDataBaseFilterInvocationSecurityMetadataSource . Ho usato DefaultFilterInvocationSecurityMetadataSource come base per utilizzare urlMatcher, per implementare il metodo support () e qualcosa del genere.

Quindi è necessario implementare questi metodi:

  • Raccolta getAttributes (object Object), in cui è ansible accedere al database, cercando l”object’ protetto (normalmente l’URL per accedere) per ottenere il permesso di ConfigAttribute (normalmente il RUOLO)

  • supporti booleani (Class clazz)

  • Raccolta getAllConfigAttributes ()

Fai attenzione con il dopo, perché è chiamato all’avvio e forse non è ben configurato in questo momento (voglio dire, con il datasources o il contesto di persistenza avviato, a seconda di cosa stai usando). La soluzione in un ambiente Web consiste nel configurare contextConfigLocation nel web.xml per caricare applicationContext.xml prima di applicationContext-security.xml

Il passaggio finale consiste nel personalizzare applicationContext-security.xml per caricare questo bean.

Per fare ciò, ho usato i bean normali in questo file invece dello spazio dei nomi di sicurezza:

         

Devi definire tutti i fagioli correlati. Per esempio:

       

So che non è una risposta ben spiegata, ma non è così difficile come sembra.

Usa semplicemente la fonte di spring come base e otterrai ciò che desideri.

Il debugging con i dati nel tuo database ti aiuterà molto.

In realtà, la spring security 3.2 non incoraggia a farlo in base a http://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/faq.html#faq-dynamic-url-metadata

ma è ansible (ma non elegante) utilizzare l’elemento http nello spazio dei nomi con un accessDecisionManager personalizzato.

La configurazione dovrebbe essere:

                           

CustomAccessDecisionManager dovrebbe essere …

 public class CustomAccessDecisionManager extends AbstractAccessDecisionManager { ... public void decide(Authentication authentication, Object filter, Collection configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { if ((filter == null) || !this.supports(filter.getClass())) { throw new IllegalArgumentException("Object must be a FilterInvocation"); } String url = ((FilterInvocation) filter).getRequestUrl(); String contexto = ((FilterInvocation) filter).getRequest().getContextPath(); Collection roles = service.getConfigAttributesFromSecuredUris(contexto, url); int deny = 0; for (AccessDecisionVoter voter : getDecisionVoters()) { int result = voter.vote(authentication, filter, roles); if (logger.isDebugEnabled()) { logger.debug("Voter: " + voter + ", returned: " + result); } switch (result) { case AccessDecisionVoter.ACCESS_GRANTED: return; case AccessDecisionVoter.ACCESS_DENIED: deny++; break; default: break; } } if (deny > 0) { throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied")); } // To get this far, every AccessDecisionVoter abstained checkAllowIfAllAbstainDecisions(); } ... } 

Dove getConfigAttributesFromSecuredUris recupera il modulo DB de ruoli per l’URL specifico

Ho lo stesso problema, fondamentalmente mi piacerebbe tenere separato l’elenco di intercettazione-url dall’altra sezione di configurazione springsecurity, il primo ad appartenere alla configurazione dell’applicazione quest’ultima alla configurazione del prodotto (core, plugin).

C’è una proposta nella JIRA di spring, riguardante questo problema.

Non voglio rinunciare a utilizzare lo spazio dei nomi di springsecurity, quindi ho pensato ad alcune possibili soluzioni per affrontare questo problema.

Per avere l’elenco di intercetta-url creato dynamicmente devi iniettare l’object securitymetadatasource in FilterSecurityInterceptor. Utilizzando lo schema springsecurity, l’istanza di FilterSecurityInterceptor viene creata dalla class HttpBuilder e non esiste alcun modo per passare securitymetadatasource come proprietà definita nel file di configurazione dello schema, tanto meno quanto usando il tipo di soluzione alternativa, che potrebbe essere:

  • Definire un filtro personalizzato, da eseguire prima di FilterSecurityInterceptor, in questo filtro che recupera l’istanza FilterSecurityInterceptor (supponendo che venga definita una sezione http univoca) dal contesto di spring e inietti l’istanza securitymetadatasource;
  • Come sopra ma in un HandlerInterceptor.

Cosa ne pensi?

Questa è la soluzione che ho applicato per dividere l’elenco delle voci di intercettazione-url dall’altra configurazione di sicurezza a molla.

  ........      

Il bean securityMetadataSource può essere inserito nello stesso file di configurazione o in un altro file di configurazione.

    

Naturalmente è ansible decidere di implementare il proprio bean securityMetadataSource implementando l’interfaccia FilterInvocationSecurityMetadataSource. Qualcosa come questo:

  

Spero che questo ti aiuti.

Una soluzione semplice che funziona per me.

   

customAuthenticationProvider è un bean

  

nel metodo di creazione della class CustomAuthenticationProvider:

 public synchronized String getReturnStringMethod() { //get data from database (call your method) if(condition){ return "IS_AUTHENTICATED_ANONYMOUSLY"; } return "ROLE_ADMIN,ROLE_USER"; } 

Ecco come può essere fatto in Spring Security 3.2:

 @Configuration @EnableWebMvcSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Bean public SecurityConfigDao securityConfigDao() { SecurityConfigDaoImpl impl = new SecurityConfigDaoImpl() ; return impl ; } @Override protected void configure(HttpSecurity http) throws Exception { /* get a map of patterns and authorities */ Map viewPermissions = securityConfigDao().viewPermissions() ; ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry interceptUrlRegistry = http .authorizeRequests().antMatchers("/publicAccess/**") .permitAll(); for (Map.Entry entry: viewPermissions.entrySet()) { interceptUrlRegistry.antMatchers(entry.getKey()).hasAuthority(entry.getValue()); } interceptUrlRegistry.anyRequest().authenticated() .and() ... /* rest of the configuration */ } }