diziaq
diziaq

Reputation: 7815

How to declaratively use authorization with JWT in Spring 5 controller?

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);
    }
}

Upvotes: 2

Views: 1393

Answers (2)

diziaq
diziaq

Reputation: 7815

After diving deeper I've found out that the question consists of the three:

  1. How to parse JWT string into an object?
  2. How to pass the object into a controller method?
  3. What is the standard/default way in sping boot to do that?

Here are the short answers:

  1. Use any library you like to convert encoded JWT into an object.
    One of them is com.auth0:java-jwt.

  2. 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).

  3. 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).

Upvotes: 1

Ken Chan
Ken Chan

Reputation: 90517

To authenticate successfully , the request must be authenticated by a AuthenticationProvider . Once it successfully authenticates , it will return a Authentication object which contain a principal object.

@AuthenticationPrincipal just helps to access this principle object. However, spring-security does not provide a AuthenticationProvider that can authenticate with JWT out of the box, that means you have to implement by yourself. In your customised AuthenticationProvider , you verify the JWT and decode the property that you are interested and create a customised principal object that hold these properties such that you can access them by @AuthenticationPrincipal.

Just have a quick google and found this example may help you.

Upvotes: 1

Related Questions