2
0
Эх сурвалжийг харах

Deserialize the principal in a neutral way

When the principal of the Authentication is an object, it is not necessarily
an User: it could be another implementation of UserDetails, or even a
completely unrelated type. Since the type of the object is serialized as a
property and used by the deserialization anyway, there's no point in
enforcing a stricter type.
Frank Pavageau 8 жил өмнө
parent
commit
6cc0f6c054

+ 1 - 2
core/src/main/java/org/springframework/security/jackson2/UsernamePasswordAuthenticationTokenDeserializer.java

@@ -26,7 +26,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.MissingNode;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.core.userdetails.User;
 
 import java.io.IOException;
 import java.util.List;
@@ -62,7 +61,7 @@ class UsernamePasswordAuthenticationTokenDeserializer extends JsonDeserializer<U
 		JsonNode principalNode = readJsonNode(jsonNode, "principal");
 		Object principal = null;
 		if(principalNode.isObject()) {
-			principal = mapper.readValue(principalNode.traverse(mapper), new TypeReference<User>() {});
+			principal = mapper.readValue(principalNode.traverse(mapper), Object.class);
 		} else {
 			principal = principalNode.asText();
 		}

+ 48 - 0
core/src/test/java/org/springframework/security/jackson2/UsernamePasswordAuthenticationTokenMixinTests.java

@@ -17,13 +17,16 @@
 package org.springframework.security.jackson2;
 
 import java.io.IOException;
+import java.util.ArrayList;
 
+import com.fasterxml.jackson.annotation.JsonClassDescription;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import org.json.JSONException;
 import org.junit.Test;
 import org.skyscreamer.jsonassert.JSONAssert;
 
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.core.userdetails.User;
 
@@ -49,6 +52,20 @@ public class UsernamePasswordAuthenticationTokenMixinTests extends AbstractMixin
 	public static final String AUTHENTICATED_STRINGPRINCIPAL_JSON = AUTHENTICATED_JSON.replace( UserDeserializerTests.USER_JSON, "\"admin\"");
 	// @formatter:on
 
+	// @formatter:off
+	private static final String NON_USER_PRINCIPAL_JSON = "{"
+		+ "\"@class\": \"org.springframework.security.jackson2.UsernamePasswordAuthenticationTokenMixinTests$NonUserPrincipal\", "
+		+ "\"username\": \"admin\""
+		+ "}";
+	// @formatter:on
+
+	// @formatter:off
+	private static final String AUTHENTICATED_NON_USER_PRINCIPAL_JSON = AUTHENTICATED_JSON
+		.replace(UserDeserializerTests.USER_JSON, NON_USER_PRINCIPAL_JSON)
+		.replaceAll(UserDeserializerTests.USER_PASSWORD, "null")
+		.replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON, SimpleGrantedAuthorityMixinTests.NO_AUTHORITIES_ARRAYLIST_JSON);
+	// @formatter:on
+
 	// @formatter:off
 	private static final String UNAUTHENTICATED_STRINGPRINCIPAL_JSON = AUTHENTICATED_STRINGPRINCIPAL_JSON
 		.replace("\"authenticated\": true, ", "\"authenticated\": false, ")
@@ -115,9 +132,40 @@ public class UsernamePasswordAuthenticationTokenMixinTests extends AbstractMixin
 		JSONAssert.assertEquals(AUTHENTICATED_JSON.replaceAll(UserDeserializerTests.USER_PASSWORD, "null"), actualJson, true);
 	}
 
+	@Test
+	public void serializeAuthenticatedUsernamePasswordAuthenticationTokenMixinWithNonUserPrincipalTest() throws JsonProcessingException, JSONException {
+		NonUserPrincipal principal = new NonUserPrincipal();
+		principal.setUsername("admin");
+		UsernamePasswordAuthenticationToken token =
+			new UsernamePasswordAuthenticationToken(principal, null, new ArrayList<GrantedAuthority>());
+		String actualJson = mapper.writeValueAsString(token);
+		JSONAssert.assertEquals(AUTHENTICATED_NON_USER_PRINCIPAL_JSON, actualJson, true);
+	}
+
+	@Test
+	public void deserializeAuthenticatedUsernamePasswordAuthenticationTokenWithNonUserPrincipalTest() throws IOException {
+		UsernamePasswordAuthenticationToken token = mapper
+			.readValue(AUTHENTICATED_NON_USER_PRINCIPAL_JSON, UsernamePasswordAuthenticationToken.class);
+		assertThat(token).isNotNull();
+		assertThat(token.getPrincipal()).isNotNull().isInstanceOf(NonUserPrincipal.class);
+	}
+
 	private UsernamePasswordAuthenticationToken createToken() {
 		User user = createDefaultUser();
 		UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
 		return token;
 	}
+
+	@JsonClassDescription
+	public static class NonUserPrincipal {
+		private String username;
+
+		public String getUsername() {
+			return username;
+		}
+
+		public void setUsername(String username) {
+			this.username = username;
+		}
+	}
 }

+ 1 - 2
web/src/main/java/org/springframework/security/web/jackson2/PreAuthenticatedAuthenticationTokenDeserializer.java

@@ -20,7 +20,6 @@ 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;
@@ -63,7 +62,7 @@ class PreAuthenticatedAuthenticationTokenDeserializer extends JsonDeserializer<P
 		JsonNode principalNode = readJsonNode(jsonNode, "principal");
 		Object principal = null;
 		if(principalNode.isObject()) {
-			principal = mapper.readValue(principalNode.traverse(mapper), new TypeReference<User>() {});
+			principal = mapper.readValue(principalNode.traverse(mapper), Object.class);
 		} else {
 			principal = principalNode.asText();
 		}