I just released the Starter Class of "Learn Spring Security":
1. Overview
In this tutorial, we’ll illustrate how to use Run-As authentication in Spring Security with a simple scenario.
The very high level explanation about Run-As is as follows: a user can execute some piece of logic as another principal with different privileges.
2. The RunAsManager
The first thing we’ll need to do is set up our GlobalMethodSecurity and inject a RunAsManager.
This is responsible for providing the temporary Authentication object with extra privileges:
@Configuration @EnableGlobalMethodSecurity(securedEnabled = true) public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration { @Override protected RunAsManager runAsManager() { RunAsManagerImpl runAsManager = new RunAsManagerImpl(); runAsManager.setKey("MyRunAsKey"); return runAsManager; } }
By overriding runAsManager, we’re replacing the default implementation in the base class – which simply returns a null.
Also notice the key property – the framework uses that to secure/verify temporary Authentication objects (created via this manager).
Finally – the resulting Authentication object is a RunAsUserToken.
3. Security Configuration
To authenticate our temporary Authentication object, we’ll set up a RunAsImplAuthenticationProvider:
@Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { ... auth.authenticationProvider(runAsAuthenticationProvider()); } @Bean public AuthenticationProvider runAsAuthenticationProvider() { RunAsImplAuthenticationProvider authProvider = new RunAsImplAuthenticationProvider(); authProvider.setKey("MyRunAsKey"); return authProvider; }
We’re of course setting this up with the same key we used in the manager – so that the provider can check that the RunAsUserToken authentication object is created using the same key.
4. The Controller With @Secured
Now – let’s see how to use Run-As Authentication replacement:
@Controller @RequestMapping("/runas") class RunAsController { @Secured({ "ROLE_USER", "RUN_AS_REPORTER" }) @RequestMapping @ResponseBody public String tryRunAs() { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); return "Current User Authorities inside this RunAS method only " + auth.getAuthorities().toString(); } }
The core thing here is the new role – RUN_AS_REPORTER. This is the trigger of the Run-As functionality – as the framework deals with it differently because of the prefix.
When a request executes through this logic, we’ll have:
- The current user authorities before tryRunAs() method are [ROLE_USER]
- The current user authorities inside tryRunAs() method are [ROLE_USER, ROLE_RUN_AS_REPORTER]
- The temporary Authentication object replaces the existing Authentication object for the duration of the tryRunAS() method invocation only
5. The Service
Finally, let’s implement the actual logic – a simple service layer that’s also secured:
@Service public class RunAsService { @Secured({ "ROLE_RUN_AS_REPORTER" }) public Authentication getCurrentUser() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); return authentication; } }
Note that:
- To access getCurrentUser() method, we need to ROLE_RUN_AS_REPORTER
- So we can only call getCurrentUser() method inside our tryRunAs() controller method
6. The Front-End
Next, we will use a simple front-end to test our Run-As feature:
<html> <body> Current user authorities: <span sec:authentication="principal.authorities">user</span> <br/> <span id="temp"></span> <a href="#" onclick="tryRunAs()">Generate Report As Super User</a> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script> <script type="text/javascript"> function tryRunAs(){ $.get( "/runas" , function( data ) { $("#temp").html(data); }); } </script> </body> </html>
So now, when a user triggers the “Generate Report As Super User” action – they’ll obtain the temporary ROLE_RUN_AS_REPORTER authority.
7. Conclusion
In this quick tutorial, we explored a simple example using the Spring Security Run-As authentication replacement feature.
This tutorial is based on the codebase available on GitHub.