I am using Spring Boot 2.2.5 with Spring Security 5.2.2 using spring-security-oauth2-resource-server
and spring-security-oauth2-jose
dependencies.
In my controller methods, I have this working:
@GetMapping
public ResponseEntity<?> doSomething(@AuthenticationPrincipal JwtAuthenticationToken principal) {
User user = getUser(principal);
...
}
The principal
gets injected and contains the id of the user on Azure (I am using Azure AD B2C).
The first thing I need to do in every method is get my own User
object using this private method that retrieves the User
from my userService
Spring bean:
private User getUser(JwtAuthenticationToken principal) {
AuthorizationServerUserId authorizationServerUserId = AuthorizationServerUserId.fromPrincipal(principal);
return userService.findByAuthorizationServerUserId(authorizationServerUserId)
.orElseThrow(() -> UserNotFoundException.forAuthorizationServerUserId(authorizationServerUserId));
}
How can I configure Spring (Boot) so that this works:
@GetMapping
public ResponseEntity<?> doSomething(@AuthenticationPrincipal User user) {
}
The security configuration is currently done like this:
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
.authorizeRequests(registry -> {
registry.antMatchers("/registration/**").permitAll();
registry.antMatchers("/api/**").authenticated();
});
}
}
@AuthenticationPrincipal
accepts a SpEL expression as a parameter.
You can specify the bean and method that transforms the principal into a User
in the provided SpEL expression.
@GetMapping
public ResponseEntity<?> doSomething(@AuthenticationPrincipal(expression = "@userService.getUser(#this)") User user) {
}
where userService
refers to the UserService
bean.
You can take it one step further by creating a custom annotation that uses @AuthenticationPrincipal
as a meta annotation.
@Target({ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@AuthenticationPrincipal(expression = "@userService.getUser(#this)")
public @interface CurrentUser {}
Then your controller would become
@GetMapping
public ResponseEntity<?> doSomething(@CurrentUser User user) {
}