He publicado a detailed article sobre la implementación de Run-As junto con @PreAuthorize
.
1) Implemente su propio RunAsManager
que crea el Authentication
para usar durante la ejecución del método en función de cualquier lógica personalizada. El siguiente ejemplo utiliza una anotación personalizada que proporciona la función adicional:
public class AnnotationDrivenRunAsManager extends RunAsManagerImpl {
@Override
public Authentication buildRunAs(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
if(!(object instanceof ReflectiveMethodInvocation) || ((ReflectiveMethodInvocation)object).getMethod().getAnnotation(RunAsRole.class) == null) {
return super.buildRunAs(authentication, object, attributes);
}
String roleName = ((ReflectiveMethodInvocation)object).getMethod().getAnnotation(RunAsRole.class).value();
if (roleName == null || roleName.isEmpty()) {
return null;
}
GrantedAuthority runAsAuthority = new SimpleGrantedAuthority(roleName);
List<GrantedAuthority> newAuthorities = new ArrayList<GrantedAuthority>();
// Add existing authorities
newAuthorities.addAll(authentication.getAuthorities());
// Add the new run-as authority
newAuthorities.add(runAsAuthority);
return new RunAsUserToken(getKey(), authentication.getPrincipal(), authentication.getCredentials(),
newAuthorities, authentication.getClass());
}
}
Esta aplicación va a buscar una anotación personalizada @RunAsRole
en un método protegido (por ejemplo @RunAsRole("ROLE_AUDITOR")
) y, si lo encuentra, se agregará la autoridad dada (ROLE_AUDITOR
en este caso) a la lista de autoridades otorgadas. RunAsRole
es una simple anotación personalizada.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RunAsRole {
String value();
}
2) una instancia del gestor:
<bean id="runAsManager"
class="org.springframework.security.access.intercept.RunAsManagerImpl">
<property name="key" value="my_run_as_key"/>
</bean>
3) verlo:
<global-method-security pre-post-annotations="enabled" run-as-manager-ref="runAsManager">
<expression-handler ref="expressionHandler"/>
</global-method-security>
4) Ejemplo de uso en un controlador:
@Controller
public class TransactionLogController {
@PreAuthorize("hasRole('ROLE_REGISTERED_USER')") //Authority needed to access the method
@RunAsRole("ROLE_AUDITOR") //Authority added by RunAsManager
@RequestMapping(value = "/transactions", method = RequestMethod.GET) //Spring MVC configuration. Not related to security
@ResponseBody //Spring MVC configuration. Not related to security
public List<Transaction> getTransactionLog(...) {
... //Invoke something in the backend requiring ROLE_AUDITOR
{
... //User does not have ROLE_AUDITOR here
}
EDIT: El valor de key
en RunAsManagerImpl
puede ser todo lo que desee. Aquí está el extracto de Spring docs sobre su uso:
Para asegurar código malicioso no crea un RunAsUserToken
y el presente que para la aceptación garantizada por el RunAsImplAuthenticationProvider
, la comprobación de una clave se almacena en todos los tokens generados. El RunAsManagerImpl
y RunAsImplAuthenticationProvider
se crea en el contexto frijol con la misma clave:
<bean id="runAsManager"
class="org.springframework.security.access.intercept.RunAsManagerImpl">
<bean id="runAsAuthenticationProvider"
class="org.springframework.security.access.intercept.RunAsImplAuthenticationProvider">
Al utilizar la misma clave , cada RunAsUserToken
se puede validar que fue creado por un aprobado RunAsManagerImpl
.El RunAsUserToken
es inmutable después de la creación de por razones de seguridad.
¿Para qué se utiliza exactamente "my_run_as_key"? - No puedo resolver esto. Probablemente leí la misma guía que citó aquí, pero esta clave no tiene ningún sentido para mí. –
@DanielBo El valor puede ser lo que quieras. Básicamente es una contraseña. Actualicé la respuesta para citar los documentos de Spring sobre por qué es necesario. – kaqqao