I published a detailed article on Run-As implementation in combination with @PreAuthorize
.
1) Deploy your own RunAsManager
, which creates Authentication
for use at run time based on any user logic. The following example uses a custom annotation that provides an additional role:
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>();
This implementation will look for the @RunAsRole
user-defined annotation for the protected method (for example, @RunAsRole("ROLE_AUDITOR")
) and, if found, will add this authority ( ROLE_AUDITOR
in this case) to the list of granted permissions. RunAsRole
itself is just a simple custom annotation.
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface RunAsRole { String value(); }
2) To issue a manager launch:
<bean id="runAsManager" class="org.springframework.security.access.intercept.RunAsManagerImpl"> <property name="key" value="my_run_as_key"/> </bean>
3) Register it:
<global-method-security pre-post-annotations="enabled" run-as-manager-ref="runAsManager"> <expression-handler ref="expressionHandler"/> </global-method-security>
4) An example of use in the controller:
@Controller public class TransactionLogController { @PreAuthorize("hasRole('ROLE_REGISTERED_USER')")
EDIT: The key
value in RunAsManagerImpl
can be whatever you want. Here is an excerpt from Spring docs on its use:
So that the malicious code does not create RunAsUserToken
and is present for guaranteed acceptance of RunAsImplAuthenticationProvider
, the key hash is stored in all generated tokens. RunAsManagerImpl
and RunAsImplAuthenticationProvider
created in a bean context with the same key:
<bean id="runAsManager" class="org.springframework.security.access.intercept.RunAsManagerImpl">
<bean id="runAsAuthenticationProvider" class="org.springframework.security.access.intercept.RunAsImplAuthenticationProvider">
Using the same key, each RunAsUserToken
can be verified; it was created approved by RunAsManagerImpl
. RunAsUserToken
unchanged after creation for security reasons.