There is a simple web service with one endpoint "GET /hello"
.
It would be good to declaratively describe in the controller that a JWT is expected in order to extract from it some data about the authorized user making the request.
Exploring some open source projects on Github, I see that the @AuthenticationPrincipal
annotation is somehow involved in the process. However, none of the tutorials I've managed to find mention such a declarative approach - they mostly show how to create a JWT, not how to deal with one.
I will be grateful if you point out noteworthy examples that I missed.
Obviously the problem is trivial and related to the basic capabilities of Spring Security, but I can't put the puzzle togeher.
Please, help me to find a proper (natural) way to pass JWT into the controller and get data from it.
Could you share a working example with dependencies and a small test showing how to work with JWT in controller?
SpringBoot 2.4.0
import org.springframework. ??? .Jwt;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
@RestController
public class MyController {
@GetMapping("hello")
public Object getRequests(@AuthenticationPrincipal Jwt jwt) {
String name = getPropertyFromJwt(jwt, "name");
String id = getPropertyFromJwt(jwt, "id");
return Map.of("name", name, "id", id);
}
}
After diving deeper I've found out that the question consists of the three:
Here are the short answers:
Use any library you like to convert encoded JWT into an object.
One of them is com.auth0:java-jwt.
Parameter annotated with @AuthenticationPrincipal
can be of any type X
and it comes from currentSecurityContext.getAuthorization().getPrincipal()
where currentSecurityContext
is of type org.springframework.security.core.context.SecurityContext
.
In WebFlux configuration it comes from invocation of ServerSecurityContextRepository.load(...)
for every request (see implementation below).
I did not manage to find the answer for the #3, but had realized that there are several open source ad-hoc implementations (good and bad) of this concern. After all I've ended up implementing another one just to understand the pitfals.
Please, find a sample minimal working project here.
The key points are:
A. Configuration using an implementation of ServerSecurityContextRepository
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
ServerSecurityContextRepository repo = createContextRepository();
return http
.authorizeExchange(e -> e.anyExchange().authenticated())
.securityContextRepository(repo)
.build();
}
}
B. Create implementation of Authentication
interface and a function (or chain of functions) ServerWebExchange -> Authentication
that should be applied in ServerSecurityContextRepository.load
method and inserted into a SecurityContext
(i.e. SecurityContextImpl
) of each server request.
C. Principal can be any object on your choice. Return it from Authentication.getPrincipal()
method of the implemetation created in (B).