spring - taglibs - sec:authorize thymeleaf
Creación de nuevos roles y permisos dinámicamente en Spring Security 3 (5)
- Cree su modelo (usuario, rol, permisos) y una forma de recuperar permisos para un usuario determinado;
- Defina su propio org.springframework.security.authentication.ProviderManager y configure (configure sus proveedores) como un org.springframework.security.authentication.AuthenticationProvider personalizado; este último debe devolver a su método de autenticación una autenticación, que debe configurarse con la GrantedAuthority, en su caso, todos los permisos para el usuario dado.
El truco en ese artículo es tener roles asignados a los usuarios, pero, para establecer los permisos para esos roles en el objeto Authentication.authorities.
Para eso le aconsejo que lea la API y vea si puede extender algún ProviderManager y AuthenticationProvider básicos en lugar de implementar todo. Lo he hecho con LdapAuthenticationProvider configurando un LdapAuthoritiesPopulator personalizado, que recuperaría los roles correctos para el usuario.
Estoy usando Spring Security 3 en el proyecto Struts 2 + Spring IOC.
He usado filtro personalizado, proveedor de autenticación, etc. en mi proyecto.
Puedes ver mi security.xml aquí
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<global-method-security pre-post-annotations="enabled">
<expression-handler ref="expressionHandler" />
</global-method-security>
<beans:bean id="expressionHandler"
class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler" >
<beans:property name="permissionEvaluator" ref="customPermissionEvaluator" />
</beans:bean>
<beans:bean class="code.permission.MyCustomPermissionEvaluator" id="customPermissionEvaluator" />
<!-- User Login -->
<http auto-config="true" use-expressions="true" pattern="/user/*" >
<intercept-url pattern="/index.jsp" access="permitAll"/>
<intercept-url pattern="/user/showLoginPage.action" access="permitAll"/>
<intercept-url pattern="/user/showFirstPage" access="hasRole(''ROLE_USER'') or hasRole(''ROLE_VISIT'')"/>
<intercept-url pattern="/user/showSecondUserPage" access="hasRole(''ROLE_USER'')"/>
<intercept-url pattern="/user/showThirdUserPage" access="hasRole(''ROLE_VISIT'')"/>
<intercept-url pattern="/user/showFirstPage" access="hasRole(''ROLE_USER'') or hasRole(''ROLE_VISIT'')"/>
<form-login login-page="/user/showLoginPage.action" />
<logout invalidate-session="true"
logout-success-url="/"
logout-url="/user/j_spring_security_logout"/>
<access-denied-handler ref="myAccessDeniedHandler" />
<custom-filter before="FORM_LOGIN_FILTER" ref="myApplicationFilter"/>
</http>
<beans:bean id="myAccessDeniedHandler" class="code.security.MyAccessDeniedHandler" />
<beans:bean id="myApplicationFilter" class="code.security.MyApplicationFilter">
<beans:property name="authenticationManager" ref="authenticationManager"/>
<beans:property name="authenticationFailureHandler" ref="failureHandler"/>
<beans:property name="authenticationSuccessHandler" ref="successHandler"/>
</beans:bean>
<beans:bean id="successHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<beans:property name="defaultTargetUrl" value="/user/showFirstPage"> </beans:property>
</beans:bean>
<beans:bean id="failureHandler"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/user/showLoginPage.action?login_error=1"/>
</beans:bean>
<beans:bean id= "myUserDetailServiceImpl" class="code.security.MyUserDetailServiceImpl">
</beans:bean>
<beans:bean id="myAuthenticationProvider" class="code.security.MyAuthenticationProvider">
<beans:property name="userDetailsService" ref="myUserDetailServiceImpl"/>
</beans:bean>
<!-- User Login Ends -->
<!-- Admin Login -->
<http auto-config="true" use-expressions="true" pattern="/admin/*" >
<intercept-url pattern="/index.jsp" access="permitAll"/>
<intercept-url pattern="/admin/showSecondLogin" access="permitAll"/>
<intercept-url pattern="/admin/*" access="hasRole(''ROLE_ADMIN'')"/>
<form-login login-page="/admin/showSecondLogin"/>
<logout invalidate-session="true"
logout-success-url="/"
logout-url="/admin/j_spring_security_logout"/>
<access-denied-handler ref="myAccessDeniedHandlerForAdmin" />
<custom-filter before="FORM_LOGIN_FILTER" ref="myApplicationFilterForAdmin"/>
</http>
<beans:bean id="myAccessDeniedHandlerForAdmin" class="code.security.admin.MyAccessDeniedHandlerForAdmin" />
<beans:bean id="myApplicationFilterForAdmin" class="code.security.admin.MyApplicationFilterForAdmin">
<beans:property name="authenticationManager" ref="authenticationManager"/>
<beans:property name="authenticationFailureHandler" ref="failureHandlerForAdmin"/>
<beans:property name="authenticationSuccessHandler" ref="successHandlerForAdmin"/>
</beans:bean>
<beans:bean id="successHandlerForAdmin"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
</beans:bean>
<beans:bean id="failureHandlerForAdmin"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/admin/showSecondLogin?login_error=1"/>
</beans:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="myAuthenticationProviderForAdmin" />
<authentication-provider ref="myAuthenticationProvider" />
</authentication-manager>
<beans:bean id="myAuthenticationProviderForAdmin" class="code.security.admin.MyAuthenticationProviderForAdmin">
<beans:property name="userDetailsService" ref="userDetailsServiceForAdmin"/>
</beans:bean>
<beans:bean id= "userDetailsServiceForAdmin" class="code.security.admin.MyUserDetailsServiceForAdminImpl">
</beans:bean>
<!-- Admin Login Ends -->
<beans:bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<beans:property name="basenames">
<beans:list>
<beans:value>code/security/SecurityMessages</beans:value>
</beans:list>
</beans:property>
</beans:bean>
Hasta ahora se puede ver, el patrón de url que he mencionado está codificado. Quería saber si hay una manera de crear nuevos ROLES y PERMISOS dinámicamente, no codificados.
Como crear nuevos roles y permisos y guardarlos en la base de datos y luego acceder desde la base de datos. He buscado en la red, pero no puedo descubrir cómo agregar nuevas entradas al código.
Así que estas son al menos dos preguntas:
- ¿Cómo hacer que las autoridades / privilegios / roles otorgados sean dinámicos?
- ¿Cómo hacer que la restricción de acceso para las URL sea dinámica?
1) ¿Cómo hacer que las autoridades / privilegios / roles otorgados sean dinámicos?
No responderé esto con gran detalle, porque creo que este tema se discutió con suficiente frecuencia.
La forma más sencilla sería almacenar la información completa del usuario (inicio de sesión, contraseña y roles) en una base de datos (3 Tablas: Usuario, Roles, Funciones de Usuario2) y usar el Servicio JdbcDetailService
. Puede configurar las dos declaraciones SQL (para autenticación y para otorgar los roles) muy bien en su configuración xml.
Pero luego el usuario debe cerrar sesión e iniciar sesión para obtener estos nuevos roles. Si esto no es aceptable, también debe manipular los roles del usuario que ha iniciado sesión actualmente. Se almacenan en la sesión de los usuarios. Supongo que la forma más sencilla de hacerlo es agregar un filtro en la cadena de filtros de seguridad de primavera que actualiza los roles para cada solicitud, si es necesario cambiarlos.
2) ¿Cómo hacer que la restricción de acceso para las URL sea dinámica?
Aquí tienes al menos dos formas:
- Al
FilterSecurityInterceptor
elFilterSecurityInterceptor
y actualizar elsecurityMetadataSource
, los Roles necesarios deben almacenarse allí. Al menos debe manipular la salida del métodoDefaultFilterInvocationSecurityMetadataSource#lookupAttributes(String url, String method)
- La otra forma sería usar otras expresiones para el atributo de
access="hasRole(''ROLE_USER'')"
lugar deaccess="hasRole(''ROLE_USER'')"
. Ejemplo:access="isAllowdForUserPages1To3"
. Por supuesto debes crear ese método. Esto se denomina un "controlador de expresiones SpEL personalizado" (si tiene el libro Spring Security 3 está alrededor de la página 210. ¡Me gustaría que tuvieran números de capítulo!). Entonces, lo que debe hacer ahora es subclaseWebSecurityExpressionRoot
e introducir un nuevo métodoisAllowdForUserPages1To3
. Luego debe subclasificarDefaultWebSecurityExpressionHandler
y modificar el métodocreateEvaluationContext
para que su primera solicitudStandartEvaluationContext
llame a super (debe enviar el resultado aStandartEvaluationContext
). Luego, reemplace elrootObject
en elStandartEvaluationContext
usando su nueva implementaciónCustomWebSecurityExpressionRoot
. Esa es la parte difícil! Luego, debe reemplazar el atributoexpressionHandler
deexpressionVoter
(WebExpressionVoter
) en la configuración xml con su nuevo subclaseDefaultWebSecurityExpressionHandler
. (Esto apesta porque primero necesita escribir mucha claridad de configuración de seguridad ya que no puede acceder a ellos directamente desde el espacio de nombres de seguridad).
Esta pregunta tiene una respuesta muy sencilla. Me pregunto por qué todavía no tienes tu respuesta.
Hay dos cosas que deben ser aclaradas al menos:
Primero , debe saber que cuando usa el espacio de nombres, automáticamente se agregarán algunos filtros a cada URL que haya escrito.
En segundo lugar , también debes saber que hace cada filtro.
De vuelta a tu pregunta:
Como quiere tener intercept-url para configurarse dinámicamente, debe eliminar esos espacios de nombres y reemplazarlos con estos filtros:
<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
<sec:filter-chain-map path-type="ant">
<sec:filter-chain pattern="/css/**" filters="none" />
<sec:filter-chain pattern="/images/**" filters="none" />
<sec:filter-chain pattern="/login.jsp*" filters="none" />
<sec:filter-chain pattern="/user/showLoginPage.action" filters="none" />
<sec:filter-chain pattern="/**"
filters="
securityContextPersistenceFilter,
logoutFilter,
authenticationProcessingFilter,
exceptionTranslationFilter,
filterSecurityInterceptor" />
</sec:filter-chain-map>
</bean>
Luego, debe inyectar su propio SecurityMetadaSource en FilterSecurityInterceptor. Vea lo siguiente:
<bean id="filterSecurityInterceptor"
class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager" />
<property name="accessDecisionManager" ref="accessDecisionManager" />
<property name="securityMetadataSource" ref="myFilterInvocationSecurityMetadataSource" />
</bean>
<bean id="myFilterInvocationSecurityMetadataSource" class="myPackage.MyFilterSecurityMetadataSource">
</bean>
Pero antes de eso, primero debes personalizar ''MyFilterSecurityMetadataSource''.
Esta clase tiene que implementar el ''DefaultFilterInvocationSecurityMetadataSource''.
Como desea tener todos los roles y URL en su base de datos, debe personalizar sus atributos de obtención.
Ahora vea el siguiente ejemplo de su implementación:
public class MyFilterSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
public List<ConfigAttribute> getAttributes(Object object) {
FilterInvocation fi = (FilterInvocation) object;
String url = fi.getRequestUrl();
List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();
attributes = getAttributesByURL(url); //Here Goes Code
return attributes;
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}
¿Ves el comentario "Aquí va tu código"? Tienes que implementar ese método tú mismo.
Yo mismo, tengo una tabla llamada URL_ACCESS que contiene ambas URL y sus roles correspondientes. Después de recibir la URL del usuario, busco en esa tabla y devuelvo su rol relacionado.
Como estoy trabajando exactamente en este tema, puede hacer cualquier pregunta ... Siempre responderé.
Me gustaría complementar la respuesta de Ralph sobre la creación de expresiones SpEL personalizadas. Sus explicaciones me ayudaron mucho en mi intento de encontrar la manera correcta de hacerlo, pero creo que es necesario ampliarlas.
Aquí hay una forma de cómo crear una expresión SpEL personalizada:
1) Crear una subclase personalizada de la clase WebSecurityExpressionRoot . En esta subclase cree un nuevo método que usará en la expresión. Por ejemplo:
public class CustomWebSecurityExpressionRoot extends WebSecurityExpressionRoot {
public CustomWebSecurityExpressionRoot(Authentication a, FilterInvocation fi) {
super(a, fi);
}
public boolean yourCustomMethod() {
boolean calculatedValue = ...;
return calculatedValue;
}
}
2) Cree una subclase personalizada de la clase DefaultWebSecurityExpressionHandler y sustituya el método createSecurityExpressionRoot (autenticación de autenticación, FilterInvocation fi) (no createEvaluationContext (...)) en ella para devolver su instancia de CustomWebSecurityExpressionRoot . Por ejemplo:
@Component(value="customExpressionHandler")
public class CustomWebSecurityExpressionHandler extends DefaultWebSecurityExpressionHandler {
@Override
protected SecurityExpressionRoot createSecurityExpressionRoot(
Authentication authentication, FilterInvocation fi) {
WebSecurityExpressionRoot expressionRoot = new CustomWebSecurityExpressionRoot(authentication, fi);
return expressionRoot;
}}
3) Defina en su spring-security.xml la referencia a su bean de controlador de expresión
<security:http access-denied-page="/error403.jsp" use-expressions="true" auto-config="false">
...
<security:expression-handler ref="customExpressionHandler"/>
</security:http>
Después de esto, puedes usar tu propia expresión personalizada en lugar de la estándar:
<security:authorize access="yourCustomMethod()">
Puede utilizar Voter para restringir dinámicamente el acceso. Consulte también Obtener las URL de intercepción de Spring Security desde la base de datos o las propiedades