Add Jackson2 Support for PreAuthenticatedAuthenticationToken
Fixes gh-4120
This commit is contained in:
parent
f97f38fd57
commit
697daeab7c
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright 2015-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.jackson2;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.MissingNode;
|
||||
|
||||
/**
|
||||
* Custom deserializer for {@link PreAuthenticatedAuthenticationToken}. At the time of deserialization
|
||||
* it will invoke suitable constructor depending on the value of <b>authenticated</b> property.
|
||||
* It will ensure that the token's state must not change.
|
||||
* <p>
|
||||
* This deserializer is already registered with {@link PreAuthenticatedAuthenticationTokenMixin} but
|
||||
* you can also registered it with your own mixin class.
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @see PreAuthenticatedAuthenticationTokenMixin
|
||||
* @since 4.2
|
||||
*/
|
||||
class PreAuthenticatedAuthenticationTokenDeserializer extends JsonDeserializer<PreAuthenticatedAuthenticationToken> {
|
||||
|
||||
/**
|
||||
* This method construct {@link PreAuthenticatedAuthenticationToken} object from serialized json.
|
||||
* @param jp the JsonParser
|
||||
* @param ctxt the DeserializationContext
|
||||
* @return the user
|
||||
* @throws IOException if a exception during IO occurs
|
||||
* @throws JsonProcessingException if an error during JSON processing occurs
|
||||
*/
|
||||
@Override
|
||||
public PreAuthenticatedAuthenticationToken deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
|
||||
PreAuthenticatedAuthenticationToken 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 PreAuthenticatedAuthenticationToken(principal, credentials, authorities);
|
||||
} else {
|
||||
token = new PreAuthenticatedAuthenticationToken(principal, credentials);
|
||||
}
|
||||
token.setDetails(readJsonNode(jsonNode, "details"));
|
||||
return token;
|
||||
}
|
||||
|
||||
private JsonNode readJsonNode(JsonNode jsonNode, String field) {
|
||||
return jsonNode.has(field) ? jsonNode.get(field) : MissingNode.getInstance();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright 2015-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.jackson2;
|
||||
|
||||
import org.springframework.security.jackson2.SecurityJackson2Modules;
|
||||
import org.springframework.security.jackson2.SimpleGrantedAuthorityMixin;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize / deserialize
|
||||
* {@link org.springframework.security.authentication.UsernamePasswordAuthenticationToken}. This class register
|
||||
* a custom deserializer {@link PreAuthenticatedAuthenticationTokenDeserializer}.
|
||||
*
|
||||
* In order to use this mixin you'll need to add 3 more mixin classes.
|
||||
* <ol>
|
||||
* <li>{@link UnmodifiableSetMixin}</li>
|
||||
* <li>{@link SimpleGrantedAuthorityMixin}</li>
|
||||
* <li>{@link UserMixin}</li>
|
||||
* </ol>
|
||||
*
|
||||
* <pre>
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.registerModule(new CoreJackson2Module());
|
||||
* </pre>
|
||||
* @author Jitendra Singh
|
||||
* @see Webackson2Module
|
||||
* @see SecurityJackson2Modules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
@JsonDeserialize(using = PreAuthenticatedAuthenticationTokenDeserializer.class)
|
||||
abstract class PreAuthenticatedAuthenticationTokenMixin {
|
||||
}
|
|
@ -20,6 +20,7 @@ import javax.servlet.http.Cookie;
|
|||
|
||||
import org.springframework.security.jackson2.SecurityJackson2Modules;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetails;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
||||
import org.springframework.security.web.csrf.DefaultCsrfToken;
|
||||
import org.springframework.security.web.savedrequest.DefaultSavedRequest;
|
||||
import org.springframework.security.web.savedrequest.SavedCookie;
|
||||
|
@ -58,5 +59,6 @@ public class WebJackson2Module extends SimpleModule {
|
|||
context.setMixInAnnotations(DefaultCsrfToken.class, DefaultCsrfTokenMixin.class);
|
||||
context.setMixInAnnotations(DefaultSavedRequest.class, DefaultSavedRequestMixin.class);
|
||||
context.setMixInAnnotations(WebAuthenticationDetails.class, WebAuthenticationDetailsMixin.class);
|
||||
context.setMixInAnnotations(PreAuthenticatedAuthenticationToken.class, PreAuthenticatedAuthenticationTokenMixin.class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright 2015-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.jackson2;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.jackson2.SimpleGrantedAuthorityMixinTests;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @since 4.2
|
||||
*/
|
||||
public class PreAuthenticatedAuthenticationTokenMixinTests extends AbstractMixinTests {
|
||||
// @formatter:off
|
||||
private static final String PREAUTH_JSON = "{"
|
||||
+ "\"@class\": \"org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken\","
|
||||
+ "\"principal\": \"principal\", "
|
||||
+ "\"credentials\": \"credentials\", "
|
||||
+ "\"authenticated\": true, "
|
||||
+ "\"details\": null, "
|
||||
+ "\"authorities\": "+ SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON
|
||||
+ "}";
|
||||
// @formatter:on
|
||||
|
||||
PreAuthenticatedAuthenticationToken expected;
|
||||
|
||||
@Before
|
||||
public void setupExpected() {
|
||||
expected = new PreAuthenticatedAuthenticationToken("principal", "credentials", AuthorityUtils.createAuthorityList("ROLE_USER"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeWhenPrincipalCredentialsAuthoritiesThenSuccess() throws JsonProcessingException, JSONException {
|
||||
String serializedJson = mapper.writeValueAsString(expected);
|
||||
JSONAssert.assertEquals(PREAUTH_JSON, serializedJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeAuthenticatedUsernamePasswordAuthenticationTokenMixinTest() throws Exception{
|
||||
PreAuthenticatedAuthenticationToken deserialized = mapper
|
||||
.readValue(PREAUTH_JSON, PreAuthenticatedAuthenticationToken.class);
|
||||
assertThat(deserialized).isNotNull();
|
||||
assertThat(deserialized.isAuthenticated()).isTrue();
|
||||
assertThat(deserialized.getAuthorities()).isEqualTo(expected.getAuthorities());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue