diff --git a/pom.xml b/pom.xml index 5f260f6..ca4a7e4 100644 --- a/pom.xml +++ b/pom.xml @@ -512,15 +512,14 @@ reactor-core true - - org.springframework.security spring-security-core - test + + org.springframework spring-web diff --git a/src/main/java/org/springframework/session/data/mongo/AbstractMongoSessionConverter.java b/src/main/java/org/springframework/session/data/mongo/AbstractMongoSessionConverter.java index 276b287..45767fb 100644 --- a/src/main/java/org/springframework/session/data/mongo/AbstractMongoSessionConverter.java +++ b/src/main/java/org/springframework/session/data/mongo/AbstractMongoSessionConverter.java @@ -22,7 +22,6 @@ import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.bson.Document; - import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.converter.GenericConverter; import org.springframework.data.domain.Sort; diff --git a/src/main/java/org/springframework/session/data/mongo/JacksonMongoSessionConverter.java b/src/main/java/org/springframework/session/data/mongo/JacksonMongoSessionConverter.java index 30cd329..6aa2c7a 100644 --- a/src/main/java/org/springframework/session/data/mongo/JacksonMongoSessionConverter.java +++ b/src/main/java/org/springframework/session/data/mongo/JacksonMongoSessionConverter.java @@ -17,13 +17,16 @@ package org.springframework.session.data.mongo; import java.io.IOException; import java.util.Collections; +import java.util.HashMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.bson.Document; - +import org.bson.json.JsonMode; +import org.bson.json.JsonWriterSettings; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; +import org.springframework.security.jackson2.SecurityJackson2Modules; import org.springframework.session.FindByIndexNameSessionRepository; import com.fasterxml.jackson.annotation.JsonAutoDetect; @@ -85,9 +88,27 @@ public class JacksonMongoSessionConverter extends AbstractMongoSessionConverter objectMapper.setPropertyNamingStrategy(new MongoIdNamingStrategy()); + objectMapper.registerModules(SecurityJackson2Modules.getModules(getClass().getClassLoader())); + objectMapper.addMixIn(MongoSession.class, MongoSessionMixin.class); + objectMapper.addMixIn(HashMap.class, HashMapMixin.class); + return objectMapper; } + /** + * Used to whitelist {@link MongoSession} for {@link SecurityJackson2Modules}. + */ + private static class MongoSessionMixin { + // Nothing special + } + + /** + * Used to whitelist {@link HashMap} for {@link SecurityJackson2Modules}. + */ + private static class HashMapMixin { + // Nothing special + } + @Override protected DBObject convert(MongoSession source) { @@ -103,8 +124,8 @@ public class JacksonMongoSessionConverter extends AbstractMongoSessionConverter @Override protected MongoSession convert(Document source) { - String json = JSON.serialize(source); - + String json = source.toJson(JsonWriterSettings.builder().outputMode(JsonMode.RELAXED).build()); + try { return this.objectMapper.readValue(json, MongoSession.class); } catch (IOException e) { diff --git a/src/test/java/org/springframework/session/data/mongo/AbstractMongoSessionConverterTest.java b/src/test/java/org/springframework/session/data/mongo/AbstractMongoSessionConverterTest.java new file mode 100644 index 0000000..c641612 --- /dev/null +++ b/src/test/java/org/springframework/session/data/mongo/AbstractMongoSessionConverterTest.java @@ -0,0 +1,145 @@ +/* + * Copyright 2017 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.session.data.mongo; + +import static org.assertj.core.api.Assertions.*; + +import java.time.Duration; + +import org.bson.Document; +import org.junit.Test; +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextImpl; +import org.springframework.session.FindByIndexNameSessionRepository; + +import com.mongodb.DBObject; + +/** + * @author Greg Turnquist + */ +public abstract class AbstractMongoSessionConverterTest { + + abstract AbstractMongoSessionConverter getMongoSessionConverter(); + + @Test + public void verifyRoundTripSerialization() throws Exception { + + // given + MongoSession toSerialize = new MongoSession(); + toSerialize.setAttribute("username", "john_the_springer"); + + // when + DBObject dbObject = convertToDBObject(toSerialize); + MongoSession deserialized = convertToSession(dbObject); + + // then + assertThat(deserialized).isEqualToComparingFieldByField(toSerialize); + } + + @Test + public void verifyRoundTripSecuritySerialization() { + + // given + MongoSession toSerialize = new MongoSession(); + String principalName = "john_the_springer"; + SecurityContextImpl context = new SecurityContextImpl(); + context.setAuthentication( + new UsernamePasswordAuthenticationToken(principalName, null)); + toSerialize.setAttribute("SPRING_SECURITY_CONTEXT", context); + + // when + DBObject serialized = convertToDBObject(toSerialize); + MongoSession deserialized = convertToSession(serialized); + + // then + assertThat(deserialized).isEqualToComparingOnlyGivenFields(toSerialize, + "id", "createdMillis", "accessedMillis", "intervalSeconds", "expireAt"); + + SecurityContextImpl springSecurityContextBefore = toSerialize.getAttribute("SPRING_SECURITY_CONTEXT"); + SecurityContextImpl springSecurityContextAfter = deserialized.getAttribute("SPRING_SECURITY_CONTEXT"); + + assertThat(springSecurityContextBefore).isEqualToComparingOnlyGivenFields(springSecurityContextAfter, + "authentication.principal", "authentication.authorities", "authentication.authenticated"); + assertThat(springSecurityContextAfter.getAuthentication().getPrincipal()).isEqualTo("john_the_springer"); + assertThat(springSecurityContextAfter.getAuthentication().getCredentials()).isNull(); + } + + @Test + public void shouldExtractPrincipalNameFromAttributes() throws Exception { + + // given + MongoSession toSerialize = new MongoSession(); + String principalName = "john_the_springer"; + toSerialize.setAttribute( + FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, + principalName); + + // when + DBObject dbObject = convertToDBObject(toSerialize); + + // then + assertThat(dbObject.get("principal")).isEqualTo(principalName); + } + + @Test + public void shouldExtractPrincipalNameFromAuthentication() throws Exception { + + // given + MongoSession toSerialize = new MongoSession(); + String principalName = "john_the_springer"; + SecurityContextImpl context = new SecurityContextImpl(); + context.setAuthentication( + new UsernamePasswordAuthenticationToken(principalName, null)); + toSerialize.setAttribute("SPRING_SECURITY_CONTEXT", context); + + // when + DBObject dbObject = convertToDBObject(toSerialize); + + // then + assertThat(dbObject.get("principal")).isEqualTo(principalName); + } + + @Test + public void sessionWrapperWithNoMaxIntervalShouldFallbackToDefaultValues() { + + // given + MongoSession toSerialize = new MongoSession(); + DBObject dbObject = convertToDBObject(toSerialize); + Document document = new Document(dbObject.toMap()); + document.remove("interval"); + + // when + MongoSession convertedSession = getMongoSessionConverter().convert(document); + + // then + assertThat(convertedSession.getMaxInactiveInterval()).isEqualTo(Duration.ofMinutes(30)); + } + + MongoSession convertToSession(DBObject session) { + return (MongoSession) getMongoSessionConverter().convert(session, + TypeDescriptor.valueOf(DBObject.class), + TypeDescriptor.valueOf(MongoSession.class)); + } + + DBObject convertToDBObject(MongoSession session) { + return (DBObject) getMongoSessionConverter().convert(session, + TypeDescriptor.valueOf(MongoSession.class), + TypeDescriptor.valueOf(DBObject.class)); + } + + +} diff --git a/src/test/java/org/springframework/session/data/mongo/JacksonMongoSessionConverterTest.java b/src/test/java/org/springframework/session/data/mongo/JacksonMongoSessionConverterTest.java index 6921433..63e9c53 100644 --- a/src/test/java/org/springframework/session/data/mongo/JacksonMongoSessionConverterTest.java +++ b/src/test/java/org/springframework/session/data/mongo/JacksonMongoSessionConverterTest.java @@ -18,7 +18,6 @@ package org.springframework.session.data.mongo; import static org.assertj.core.api.Assertions.*; import org.junit.Test; - import org.springframework.data.mongodb.core.query.Query; import com.mongodb.DBObject; @@ -27,9 +26,14 @@ import com.mongodb.DBObject; * @author Jakub Kubrynski * @author Greg Turnquist */ -public class JacksonMongoSessionConverterTest { +public class JacksonMongoSessionConverterTest extends AbstractMongoSessionConverterTest { - JacksonMongoSessionConverter sut = new JacksonMongoSessionConverter(); + JacksonMongoSessionConverter mongoSessionConverter = new JacksonMongoSessionConverter(); + + @Override + AbstractMongoSessionConverter getMongoSessionConverter() { + return this.mongoSessionConverter; + } @Test public void shouldSaveIdField() throws Exception { @@ -38,7 +42,7 @@ public class JacksonMongoSessionConverterTest { MongoSession session = new MongoSession(); //when - DBObject convert = this.sut.convert(session); + DBObject convert = this.mongoSessionConverter.convert(session); //then assertThat(convert.get("_id")).isEqualTo(session.getId()); @@ -49,9 +53,11 @@ public class JacksonMongoSessionConverterTest { public void shouldQueryAgainstAttribute() throws Exception { //when - Query cart = this.sut.getQueryForIndex("cart", "my-cart"); + Query cart = this.mongoSessionConverter.getQueryForIndex("cart", "my-cart"); //then assertThat(cart.getQueryObject().get("attrs.cart")).isEqualTo("my-cart"); } + + } diff --git a/src/test/java/org/springframework/session/data/mongo/JdkMongoSessionConverterTest.java b/src/test/java/org/springframework/session/data/mongo/JdkMongoSessionConverterTest.java index 54b0fde..f03e519 100644 --- a/src/test/java/org/springframework/session/data/mongo/JdkMongoSessionConverterTest.java +++ b/src/test/java/org/springframework/session/data/mongo/JdkMongoSessionConverterTest.java @@ -15,31 +15,27 @@ */ package org.springframework.session.data.mongo; -import static org.assertj.core.api.Assertions.*; - import java.time.Duration; -import org.bson.Document; import org.junit.Test; -import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.serializer.support.DeserializingConverter; import org.springframework.core.serializer.support.SerializingConverter; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.context.SecurityContextImpl; -import org.springframework.session.FindByIndexNameSessionRepository; - -import com.mongodb.DBObject; /** * @author Jakub Kubrynski * @author Rob Winch * @author Greg Turnquist */ -public class JdkMongoSessionConverterTest { +public class JdkMongoSessionConverterTest extends AbstractMongoSessionConverterTest { Duration inactiveInterval = Duration.ofMinutes(30); JdkMongoSessionConverter mongoSessionConverter = new JdkMongoSessionConverter(inactiveInterval); + @Override + AbstractMongoSessionConverter getMongoSessionConverter() { + return this.mongoSessionConverter; + } + @Test(expected = IllegalArgumentException.class) public void constructorNullSerializer() { new JdkMongoSessionConverter(null, new DeserializingConverter(), inactiveInterval); @@ -49,82 +45,4 @@ public class JdkMongoSessionConverterTest { public void constructorNullDeserializer() { new JdkMongoSessionConverter(new SerializingConverter(), null, inactiveInterval); } - - @Test - public void verifyRoundTripSerialization() throws Exception { - - // given - MongoSession toSerialize = new MongoSession(); - toSerialize.setAttribute("username", "john_the_springer"); - - // when - DBObject dbObject = convertToDBObject(toSerialize); - MongoSession deserialized = convertToSession(dbObject); - - // then - assertThat(deserialized).isEqualToComparingFieldByField(toSerialize); - } - - @Test - public void shouldExtractPrincipalNameFromAttributes() throws Exception { - - // given - MongoSession toSerialize = new MongoSession(); - String principalName = "john_the_springer"; - toSerialize.setAttribute( - FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, - principalName); - - // when - DBObject dbObject = convertToDBObject(toSerialize); - - // then - assertThat(dbObject.get("principal")).isEqualTo(principalName); - } - - @Test - public void shouldExtractPrincipalNameFromAuthentication() throws Exception { - - // given - MongoSession toSerialize = new MongoSession(); - String principalName = "john_the_springer"; - SecurityContextImpl context = new SecurityContextImpl(); - context.setAuthentication( - new UsernamePasswordAuthenticationToken(principalName, null)); - toSerialize.setAttribute("SPRING_SECURITY_CONTEXT", context); - - // when - DBObject dbObject = convertToDBObject(toSerialize); - - // then - assertThat(dbObject.get("principal")).isEqualTo(principalName); - } - - @Test - public void sessionWrapperWithNoMaxIntervalShouldFallbackToDefaultValues() { - - // given - MongoSession toSerialize = new MongoSession(); - DBObject dbObject = convertToDBObject(toSerialize); - Document document = new Document(dbObject.toMap()); - document.remove("interval"); - - // when - MongoSession convertedSession = this.mongoSessionConverter.convert(document); - - // then - assertThat(convertedSession.getMaxInactiveInterval()).isEqualTo(Duration.ofMinutes(30)); - } - - MongoSession convertToSession(DBObject session) { - return (MongoSession) this.mongoSessionConverter.convert(session, - TypeDescriptor.valueOf(DBObject.class), - TypeDescriptor.valueOf(MongoSession.class)); - } - - DBObject convertToDBObject(MongoSession session) { - return (DBObject) this.mongoSessionConverter.convert(session, - TypeDescriptor.valueOf(MongoSession.class), - TypeDescriptor.valueOf(DBObject.class)); - } }