Skip to content

JacksonSerializer does not respect JsonTypeInfo #1065

Description

@beroal

To Reproduce

final MacAlgorithm macAlgorithm = Jwts.SIG.HS256;
final SecretKey secretKey = macAlgorithm.key().build();

final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.addMixIn(SimpleGrantedAuthority.class, SimpleGrantedAuthorityMixin.class);
final String NAME = "additional";
final JacksonSerializer jacksonSerializer = new JacksonSerializer(objectMapper);
final JacksonDeserializer jacksonDeserializer = new JacksonDeserializer(
    objectMapper,
    Map.of(NAME, SimpleGrantedAuthority.class)
);

final JwtBuilder builder = Jwts.builder().json(jacksonSerializer);
builder.claims().add(NAME, new SimpleGrantedAuthority("ROLE_ADMIN"));
final String jwt = builder.signWith(secretKey, macAlgorithm).compact();

final JwtParser parser = Jwts.parser().json(jacksonDeserializer).verifyWith(secretKey).build();
final Object payload = parser.parse(jwt).getPayload();
System.out.println(new String((byte[]) payload));

The above program prints

{"additional":{"authority":"ROLE_ADMIN"}}

In the above program, JwtParser returns a byte[] payload.

Expected behavior

It should return Claims.

Explanation

The reason is that Jackson can't parse the payload because the JSON object corresponding to SimpleGrantedAuthority doesn't contain the "@class" property. The exception that Jackson throws is

com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve subtype of [simple type, class org.springframework.security.core.authority.SimpleGrantedAuthority]: missing type id property '@class'  at [Source: UNKNOWN; line: -1, column: -1]

The SimpleGrantedAuthorityMixin has the following annotation on it:

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)

so the JSON object should contain the "@class" property.

BTW, JJWT drops this exception. This makes harder to understand why parsing of the payload failed.

For reference:

public final class SimpleGrantedAuthority implements GrantedAuthority {

    private static final long serialVersionUID = 620L;

    private final String role;

    public SimpleGrantedAuthority(String authority) {
        Assert.hasText(authority, "A granted authority textual representation is required");
        this.role = authority;
    }

    @Override
    public String getAuthority() {
        return this.role;
    }

    @Override
    public boolean equals(@Nullable Object obj) {
        /* skipped */
    }

    @Override
    public int hashCode() {
        return this.role.hashCode();
    }

    @Override
    public String toString() {
        return this.role;
    }

}

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE,
        getterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY, isGetterVisibility = JsonAutoDetect.Visibility.NONE)
public abstract class SimpleGrantedAuthorityMixin {

    @JsonCreator
    public SimpleGrantedAuthorityMixin(@JsonProperty("authority") String role) {
    }

}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions