We've opened a new role Backend Java/Spring Team Lead with Integration Experience. Part-time and entirely remote, of course.
Spring Security – Run-As Authentication
Last updated: August 19, 2020
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 do to add security to the service layer is create a MethodSecurityConfig class, which will be responsible for providing the temporary Authentication object with extra privileges. To do this, first, we’ll annotate the class with @Configuration and @EnableMethodSecurity annotations. Then, we’ll create a RunAsManager bean inside it:
@Configuration
@EnableMethodSecurity(securedEnabled = true)
public class MethodSecurityConfig{
@Bean
protected RunAsManager runAsManager() {
RunAsManagerImpl runAsManager = new RunAsManagerImpl();
runAsManager.setKey("MyRunAsKey");
return runAsManager;
}
}
This will inject our runAsManager in the spring context, replacing the default implementation, which 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.
Note: Authentication is now separated from authorization in Spring Security. RunAsManager is only used by now-deprecated components. But there is not yet an equivalent replacement in Spring Security
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:
@RestController
@RequestMapping("/runas")
class RunAsController {
@Secured({ "ROLE_USER", "RUN_AS_REPORTER" })
@RequestMapping
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 the .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.
As always, the complete code for the article is available over on GitHub.