¿Cómo decidir dinámicamente el valor del atributo de acceso en Spring Security?

En Spring Security utilizamos la etiqueta intercept-url para definir el acceso para las URL de la siguiente manera:

  

Esto está codificado en applicationContext-security.xml . En su lugar, quiero leer los valores de acceso de una tabla de base de datos. UserDetailsService mi propio UserDetailsService y leí las funciones para el usuario que UserDetailsService sesión en la base de datos. ¿Cómo asigno estos roles a los patrones de URL durante el tiempo de ejecución?

La clase FilterInvocationSecurityMetadataSourceParser en Spring-security (intente Ctrl / Cmd + Shift + T en STS con el código fuente) analiza las tags intercept-url y crea instancias de ExpressionBasedFilterInvocationSecurityMetadataSource, que extiende DefaultFilterInvocationSecurityMetadataSource que implementa FilterInvocationSecurityMetadataSource que amplía SecurityMetadataSource.

Lo que hice fue crear una clase personalizada que implemente FilterInvocationSecurityMetadataSource , OptionsFromDataBaseFilterInvocationSecurityMetadataSource . Usé DefaultFilterInvocationSecurityMetadataSource como base para usar urlMatcher, para implementar el método support () y algo así.

Entonces debes implementar estos métodos:

  • Colección getAttributes (Objeto Object), donde se puede acceder a la base de datos, buscando el ‘objeto’ que se está asegurando (normalmente la URL a la que acceder) para obtener los atributos permitidos de ConfigAttribute (normalmente los ROLE)

  • soportes booleanos (Class clazz)

  • Colección getAllConfigAttributes ()

Tenga cuidado con el último, porque se llama al inicio y tal vez no está bien configurado en este momento (es decir, con los orígenes de datos o el contexto de persistencia conectados automáticamente, según lo que esté usando). La solución en un entorno web es configurar el contextConfigLocation en el web.xml para cargar el applicationContext.xml antes de la aplicaciónContext-security.xml

El último paso es personalizar la aplicaciónContext-security.xml para cargar este bean.

Para hacer eso, usé beans regulares en este archivo en lugar del espacio de nombres de seguridad:

         

Debes definir todos los beans relacionados. Por ejemplo:

       

Sé que no es una respuesta bien explicada, pero no es tan difícil como parece.

Simplemente use la fuente de resorte como base y obtendrá lo que quiera.

La depuración con los datos en su base de datos, le ayudará mucho.

En realidad, spring security 3.2 no recomienda hacer esto de acuerdo con http://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/faq.html#faq-dynamic-url-metadata

pero, es posible (pero no elegante) usar el elemento http en el espacio de nombres con un accessDecisionManager personalizado.

La configuración debe ser:

     

CustomAccessDecisionManager debería ser …

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

Donde getConfigAttributesFromSecuredUris recupera las funciones de formulario DB de la URL específica

Tengo casi el mismo problema, básicamente me gustaría mantener separada la lista de URL de intercepción de la otra sección de configuración de seguridad de spring, la primera en pertenecer a la configuración de la aplicación, la última a la configuración del producto (núcleo, complemento).

Hay una propuesta en la JIRA de spring, sobre este problema.

No quiero renunciar al uso del espacio de nombres de seguridad de spring, así que estaba pensando en algunas soluciones posibles para enfrentar esto.

Para tener la lista de URL de intercepción creada dinámicamente, debe inyectar el objeto securitymetadatasource en FilterSecurityInterceptor. Con el esquema springsecurity, la clase HttpBuilder crea la instancia de FilterSecurityInterceptor y no hay forma de pasar el securitymetadatasource como propiedad definida en el archivo de configuración del esquema, y ​​menos el tipo de solución alternativa, que podría ser:

  • Defina un filtro personalizado, que se ejecutará antes de FilterSecurityInterceptor, en este filtro recuperando la instancia FilterSecurityInterceptor (suponiendo que se define una sección http única) por el contexto de spring e inyecte allí la instancia securitymetadatasource;
  • Lo mismo que arriba, pero en HandlerInterceptor.

¿Qué piensas?

Esta es la solución que he aplicado para dividir la lista de entradas de URL de intercepción de la otra configuración de seguridad de spring.

  ........      

Bean securityMetadataSource se puede colocar en el mismo archivo de configuración o en otro archivo de configuración.

    

Por supuesto, puede decidir implementar su propio bean securityMetadataSource implementando la interfaz FilterInvocationSecurityMetadataSource. Algo como esto:

  

Espero que esto ayude.

Una solución simple que funciona para mí.

   

customAuthenticationProvider es un bean

  

en el método de creación de clase CustomAuthenticationProvider:

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

Así es como se puede hacer en 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 */ } }