Currently I'm using spring security and @PreAuthorize
annotations to secure method calls. Now I want to change the authentication token for a method call like the run-as authentication replacement of spring security allows me to do.
Can I configure the replacement on a per method base? Per annotation, SpEL expression.... If not, would it be possible do figure out in the runAsManager what method is called? How would I configure the security config attributes for a secured object, at all?
I've posted a detailed article on implementing Run-As in conjunction with @PreAuthorize
.
1) Implement your own RunAsManager
that creates the Authentication
to use during method execution based on any custom logic. The example below uses a custom annotation that provides the extra 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>();
// 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());
}
}
This implementation will look for a custom @RunAsRole
annotation on a protected method (e.g. @RunAsRole("ROLE_AUDITOR")
) and, if found, will add the given authority (ROLE_AUDITOR
in this case) to the list of granted authorities. RunAsRole
itself is just a simple custom annotation.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RunAsRole {
String value();
}
2) Instantiate the manager:
<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) Example usage in a Controller:
@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:
The value of key
in RunAsManagerImpl
can be anything you want. Here's the excerpt from Spring docs on its use:
To ensure malicious code does not create a
RunAsUserToken
and present it for guaranteed acceptance by theRunAsImplAuthenticationProvider
, the hash of a key is stored in all generated tokens. TheRunAsManagerImpl
andRunAsImplAuthenticationProvider
is created in the 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">
By using the same key, each
RunAsUserToken
can be validated it was created by an approvedRunAsManagerImpl
. TheRunAsUserToken
is immutable after creation for security reasons.
I solved this by implementing my own RunAsManager
that checks for a custom annotation on the invoked method and returns the appropriate Token.
works great.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With