Skip to content

UsernamePasswordAuthenticationTokenDeserializer doesn't deserialize details to correct type #7482

Closed
@buzzerrookie

Description

@buzzerrookie

UsernamePasswordAuthenticationTokenDeserializer doesn't deserialize details to correct type

When using Spring Security and Spring Session with GenericJackson2JsonRedisSerializer, UsernamePasswordAuthenticationTokenDeserializer deserializes the details field of UsernamePasswordAuthenticationToken as a JsonNode, not the original Object such as WebAuthenticationDetails.

Actual Behavior

the details field of UsernamePasswordAuthenticationToken is deserialized as a com.fasterxml.jackson.databind.node.ObjectNode.

Expected Behavior

the details field of UsernamePasswordAuthenticationToken should be deserialized as a object of type @ class.

Configuration

I replace the default JdkSerializationRedisSerializer with GenericJackson2JsonRedisSerializer.

@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter  {

    // other beans and methods omitted

    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModules(new CoreJackson2Module(), new WebJackson2Module());
        return new GenericJackson2JsonRedisSerializer(mapper);
    }
}

Version

I'm using Spring Security 4.2.2, but I also find the same issue in master branch.

Sample

The relevant code is as follows, and I add some comments in it.

class UsernamePasswordAuthenticationTokenDeserializer extends JsonDeserializer<UsernamePasswordAuthenticationToken> {

    @Override
    public UsernamePasswordAuthenticationToken deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException,
            JsonProcessingException {
        UsernamePasswordAuthenticationToken token = null;
        ObjectMapper mapper = (ObjectMapper) jp.getCodec();
        JsonNode jsonNode = mapper.readTree(jp);
        Boolean authenticated = readJsonNode(jsonNode, "authenticated").asBoolean();
        JsonNode principalNode = readJsonNode(jsonNode, "principal");
        Object principal = null;
        if(principalNode.isObject()) {
            principal = mapper.readValue(principalNode.toString(), new TypeReference<User>() {});
        } else {
            principal = principalNode.asText();
        }
        Object credentials = readJsonNode(jsonNode, "credentials").asText();
        List<GrantedAuthority> authorities = mapper.readValue(
                readJsonNode(jsonNode, "authorities").toString(), new TypeReference<List<GrantedAuthority>>() {
                });
        if (authenticated) {
            token = new UsernamePasswordAuthenticationToken(principal, credentials, authorities);
        } else {
            token = new UsernamePasswordAuthenticationToken(principal, credentials);
        }
        token.setDetails(readJsonNode(jsonNode, "details")); // it just makes details deserialized as a JsonNode
        return token;
    }

    private JsonNode readJsonNode(JsonNode jsonNode, String field) {
        return jsonNode.has(field) ? jsonNode.get(field) : MissingNode.getInstance();
    }
}

the following code works.

JsonNode detailsNode = readJsonNode(jsonNode, "details");
Object details = mapper.readValue(detailsNode.toString(), new TypeReference<Object>() {});
token.setDetails(details);

Metadata

Metadata

Assignees

Labels

in: coreAn issue in spring-security-coretype: breaks-passivityA change that breaks passivity with the previous release

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions