Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change security-context for method call with spring-security

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?

like image 547
Laures Avatar asked Jul 26 '12 10:07

Laures


2 Answers

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 the RunAsImplAuthenticationProvider, the hash of a key is stored in all generated tokens. The RunAsManagerImpl and RunAsImplAuthenticationProvider 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 approved RunAsManagerImpl. The RunAsUserToken is immutable after creation for security reasons.

like image 122
kaqqao Avatar answered Oct 03 '22 19:10

kaqqao


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.

like image 45
Laures Avatar answered Oct 03 '22 20:10

Laures