14

I have created a Spring JWT authorization application. JWT contains some custom claims. On a resource server side, I wonder, where should I parse the JWT token to collect and check these claims? Should I do this in a controller or in some filter? Whats the best practice? Maybe you have some example?

dplesa
  • 1,355
  • 6
  • 23
  • 56
  • Can you elaborate on the purpose of these custom claims? By default Spring Security looks for the authorities claim, which you can populate with roles or other permissions and then protect methods and endpoints with the @PreAuthorize annotation. – Kyle Anderson Jul 21 '17 at 20:51
  • 1
    I guess, your custom claims in JWT token must be known to your application. Ideally, It should be part of your Filter to extract the authentication values and set in Spring Security context. From where, it would be taken by Auth Manager to check. Later all things can be taken care by Spring security itself, As @punkrocker27ka suggested. Have a look at my implementation for idea: https://github.com/deepak-java/jwt-spring-boot – Deepak Singh Jul 23 '17 at 05:55
  • @punkrocker27ka I need custom claims to cover some business logic, default claims are not enough. Deepak Singh thanks for the implementation, that will help me! – dplesa Jul 24 '17 at 07:52
  • @KyleAnderson, how did you know Spring looks for the "authorities" claim by default? Can you reference the source or samples? I tried this approach and it didn't work. As far as I know, Spring Security only extracts authorities from the "scope" claim automatically. Everything else should be manually mapped by using "JwtAuthenticationConverter" for example. – skryvets May 03 '19 at 19:35

3 Answers3

11

You can use a combination of a Jackson Object Mapper and Spring Security classes, namely Jwt, JwtHelper and Authentication. You can get the authentication by using Spring Security's static context object and then parse the token you receive using the JwtHelper.

ObjectMapper objectMapper = new ObjectMapper();
Authentication authentication = 
SecurityContextHolder.getContext().getAuthentication();
Map<String, Object> map = 
objectMapper.convertValue(authentication.getDetails(), Map.class);

// create a token object to represent the token that is in use.
Jwt jwt = JwtHelper.decode((String) map.get("tokenValue"));

// jwt.getClaims() will return a JSON object of all the claims in your token
// Convert claims JSON object into a Map so we can get the value of a field
Map<String, Object> claims = objectMapper.readValue(jwt.getClaims(), Map.class);
String customField = (String) claims.get("you_custom_field_name");

I would suggest debugging and putting a breakpoint on the third line in the code above. At that point, expose the authentication object. I might have some useful details you'll need later.

This can all be done on the controller. I'm not sure how to use the filter to do so.

Chillz
  • 199
  • 5
2

you can also use springframework.boot.json.JsonParser:

JsonParser parser = JsonParserFactory.getJsonParser();
Map<String, ?> tokenData = parser.parseMap(JwtHelper.decode(token).getClaims());

> tokenData.get("VALID_KEY");
Lucas
  • 49
  • 3
2

I'm using this:

private Claim getClaim(String claimKey) {
    Authentication token = SecurityContextHolder.getContext().getAuthentication();
    try {
        DecodedJWT jwt = JWT.decode(token.getCredentials().toString());
        return jwt.getClaim(claimKey);
    } catch (JWTVerificationException ex) {
        throw new RuntimeException(ex);
    }
}
Stephen Paul
  • 37,253
  • 15
  • 92
  • 74
  • 3
    The only `JWT` I can find is `com.nimbusds.jwt.JWT` which is an interface with no `decode` method. Please elaborate in your answer? – Frans May 25 '20 at 16:24
  • worked in my case with com.auth0.jwt.JWT and com.auth0.jwt.interfaces.DecodedJWT as part of java-jwt-3.3.0.jar – phirzel Nov 28 '20 at 07:57