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 d956fd3..1ff42f2 100644 --- a/src/main/java/org/springframework/session/data/mongo/AbstractMongoSessionConverter.java +++ b/src/main/java/org/springframework/session/data/mongo/AbstractMongoSessionConverter.java @@ -30,8 +30,10 @@ import org.springframework.data.mongodb.core.index.IndexInfo; import org.springframework.data.mongodb.core.index.IndexOperations; import org.springframework.data.mongodb.core.query.Query; import org.springframework.lang.Nullable; +import org.springframework.session.DelegatingIndexResolver; import org.springframework.session.FindByIndexNameSessionRepository; -import org.springframework.session.Session; +import org.springframework.session.IndexResolver; +import org.springframework.session.PrincipalNameIndexResolver; import com.mongodb.DBObject; @@ -49,6 +51,8 @@ public abstract class AbstractMongoSessionConverter implements GenericConverter private static final Log LOG = LogFactory.getLog(AbstractMongoSessionConverter.class); private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT"; + private IndexResolver indexResolver = new DelegatingIndexResolver<>(new PrincipalNameIndexResolver<>()); + /** * Returns query to be executed to return sessions based on a particular index. * @@ -80,15 +84,9 @@ public abstract class AbstractMongoSessionConverter implements GenericConverter .ensureIndex(new Index(EXPIRE_AT_FIELD_NAME, Sort.Direction.ASC).named(EXPIRE_AT_FIELD_NAME).expire(0)); } - protected String extractPrincipal(Session expiringSession) { - - String resolvedPrincipal = AuthenticationParser.extractName(expiringSession.getAttribute(SPRING_SECURITY_CONTEXT)); - - if (resolvedPrincipal != null) { - return resolvedPrincipal; - } else { - return expiringSession.getAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME); - } + protected String extractPrincipal(MongoSession expiringSession) { + return this.indexResolver.resolveIndexesFor(expiringSession) + .get(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME); } public Set getConvertibleTypes() { @@ -116,4 +114,8 @@ public abstract class AbstractMongoSessionConverter implements GenericConverter protected abstract DBObject convert(MongoSession session); protected abstract MongoSession convert(Document sessionWrapper); + + public void setIndexResolver(IndexResolver indexResolver) { + this.indexResolver = Assert.requireNonNull(indexResolver, "indexResolver must not be null!"); + } } diff --git a/src/main/java/org/springframework/session/data/mongo/AuthenticationParser.java b/src/main/java/org/springframework/session/data/mongo/AuthenticationParser.java deleted file mode 100644 index 6c65abc..0000000 --- a/src/main/java/org/springframework/session/data/mongo/AuthenticationParser.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2014-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 - * - * https://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 org.springframework.expression.Expression; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.lang.Nullable; - -/** - * Utility class to extract principal name from {@code Authentication} object. - * - * @author Jakub Kubrynski - * @author Greg Turnquist - */ -final class AuthenticationParser { - - private static final String NAME_EXPRESSION = "authentication?.name"; - - private static final SpelExpressionParser PARSER = new SpelExpressionParser(); - - private AuthenticationParser() {} - - /** - * Extracts principal name from authentication. - * - * @param authentication Authentication object - * @return principal name - */ - @Nullable - static String extractName(@Nullable Object authentication) { - - if (authentication == null) { - return null; - } - - Expression expression = PARSER.parseExpression(NAME_EXPRESSION); - - return expression.getValue(authentication, String.class); - } -} diff --git a/src/main/java/org/springframework/session/data/mongo/config/annotation/web/http/MongoHttpSessionConfiguration.java b/src/main/java/org/springframework/session/data/mongo/config/annotation/web/http/MongoHttpSessionConfiguration.java index e6614d0..754385f 100644 --- a/src/main/java/org/springframework/session/data/mongo/config/annotation/web/http/MongoHttpSessionConfiguration.java +++ b/src/main/java/org/springframework/session/data/mongo/config/annotation/web/http/MongoHttpSessionConfiguration.java @@ -31,11 +31,13 @@ import org.springframework.core.serializer.support.DeserializingConverter; import org.springframework.core.serializer.support.SerializingConverter; import org.springframework.core.type.AnnotationMetadata; import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.session.IndexResolver; import org.springframework.session.config.SessionRepositoryCustomizer; import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration; import org.springframework.session.data.mongo.AbstractMongoSessionConverter; import org.springframework.session.data.mongo.JdkMongoSessionConverter; import org.springframework.session.data.mongo.MongoIndexedSessionRepository; +import org.springframework.session.data.mongo.MongoSession; import org.springframework.util.StringUtils; import org.springframework.util.StringValueResolver; @@ -57,6 +59,7 @@ public class MongoHttpSessionConfiguration extends SpringHttpSessionConfiguratio private StringValueResolver embeddedValueResolver; private List> sessionRepositoryCustomizers; private ClassLoader classLoader; + private IndexResolver indexResolver; @Bean public MongoIndexedSessionRepository mongoSessionRepository(MongoOperations mongoOperations) { @@ -66,10 +69,19 @@ public class MongoHttpSessionConfiguration extends SpringHttpSessionConfiguratio if (this.mongoSessionConverter != null) { repository.setMongoSessionConverter(this.mongoSessionConverter); + + if (this.indexResolver != null) { + this.mongoSessionConverter.setIndexResolver(this.indexResolver); + } } else { JdkMongoSessionConverter mongoSessionConverter = new JdkMongoSessionConverter(new SerializingConverter(), new DeserializingConverter(this.classLoader), Duration.ofSeconds(MongoIndexedSessionRepository.DEFAULT_INACTIVE_INTERVAL)); + + if (this.indexResolver != null) { + mongoSessionConverter.setIndexResolver(this.indexResolver); + } + repository.setMongoSessionConverter(mongoSessionConverter); } @@ -129,4 +141,8 @@ public class MongoHttpSessionConfiguration extends SpringHttpSessionConfiguratio this.embeddedValueResolver = resolver; } + @Autowired(required = false) + public void setIndexResolver(IndexResolver indexResolver) { + this.indexResolver = indexResolver; + } } diff --git a/src/main/java/org/springframework/session/data/mongo/config/annotation/web/reactive/ReactiveMongoWebSessionConfiguration.java b/src/main/java/org/springframework/session/data/mongo/config/annotation/web/reactive/ReactiveMongoWebSessionConfiguration.java index 100f81f..3443ff7 100644 --- a/src/main/java/org/springframework/session/data/mongo/config/annotation/web/reactive/ReactiveMongoWebSessionConfiguration.java +++ b/src/main/java/org/springframework/session/data/mongo/config/annotation/web/reactive/ReactiveMongoWebSessionConfiguration.java @@ -32,10 +32,12 @@ import org.springframework.core.serializer.support.SerializingConverter; import org.springframework.core.type.AnnotationMetadata; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.ReactiveMongoOperations; +import org.springframework.session.IndexResolver; import org.springframework.session.config.ReactiveSessionRepositoryCustomizer; import org.springframework.session.config.annotation.web.server.SpringWebSessionConfiguration; import org.springframework.session.data.mongo.AbstractMongoSessionConverter; import org.springframework.session.data.mongo.JdkMongoSessionConverter; +import org.springframework.session.data.mongo.MongoSession; import org.springframework.session.data.mongo.ReactiveMongoSessionRepository; import org.springframework.util.StringUtils; import org.springframework.util.StringValueResolver; @@ -58,6 +60,8 @@ public class ReactiveMongoWebSessionConfiguration extends SpringWebSessionConfig @Autowired(required = false) private MongoOperations mongoOperations; private ClassLoader classLoader; + private IndexResolver indexResolver; + @Bean public ReactiveMongoSessionRepository reactiveMongoSessionRepository(ReactiveMongoOperations operations) { @@ -66,10 +70,20 @@ public class ReactiveMongoWebSessionConfiguration extends SpringWebSessionConfig if (this.mongoSessionConverter != null) { repository.setMongoSessionConverter(this.mongoSessionConverter); + + if (this.indexResolver != null) { + this.mongoSessionConverter.setIndexResolver(this.indexResolver); + } + } else { JdkMongoSessionConverter mongoSessionConverter = new JdkMongoSessionConverter(new SerializingConverter(), new DeserializingConverter(this.classLoader), Duration.ofSeconds(ReactiveMongoSessionRepository.DEFAULT_INACTIVE_INTERVAL)); + + if (this.indexResolver != null) { + mongoSessionConverter.setIndexResolver(this.indexResolver); + } + repository.setMongoSessionConverter(mongoSessionConverter); } @@ -146,4 +160,9 @@ public class ReactiveMongoWebSessionConfiguration extends SpringWebSessionConfig ObjectProvider> sessionRepositoryCustomizers) { this.sessionRepositoryCustomizers = sessionRepositoryCustomizers.orderedStream().collect(Collectors.toList()); } + + @Autowired(required = false) + public void setIndexResolver(IndexResolver indexResolver) { + this.indexResolver = indexResolver; + } } diff --git a/src/test/java/org/springframework/session/data/mongo/AuthenticationParserTest.java b/src/test/java/org/springframework/session/data/mongo/AuthenticationParserTest.java deleted file mode 100644 index 4aae170..0000000 --- a/src/test/java/org/springframework/session/data/mongo/AuthenticationParserTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2014-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 - * - * https://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 org.junit.jupiter.api.Test; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.context.SecurityContextImpl; - -/** - * @author Jakub Kubrynski - * @author Greg Turnquist - */ -public class AuthenticationParserTest { - - @Test - public void shouldExtractName() { - - // given - String principalName = "john_the_springer"; - SecurityContextImpl context = new SecurityContextImpl(); - context.setAuthentication(new UsernamePasswordAuthenticationToken(principalName, null)); - - // when - String extractedName = AuthenticationParser.extractName(context); - - // then - assertThat(extractedName).isEqualTo(principalName); - } - -} diff --git a/src/test/java/org/springframework/session/data/mongo/config/annotation/web/http/MongoHttpSessionConfigurationTest.java b/src/test/java/org/springframework/session/data/mongo/config/annotation/web/http/MongoHttpSessionConfigurationTest.java index 9a55375..2f73285 100644 --- a/src/test/java/org/springframework/session/data/mongo/config/annotation/web/http/MongoHttpSessionConfigurationTest.java +++ b/src/test/java/org/springframework/session/data/mongo/config/annotation/web/http/MongoHttpSessionConfigurationTest.java @@ -33,9 +33,12 @@ import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.index.IndexOperations; import org.springframework.mock.env.MockEnvironment; +import org.springframework.session.IndexResolver; import org.springframework.session.config.SessionRepositoryCustomizer; import org.springframework.session.data.mongo.AbstractMongoSessionConverter; +import org.springframework.session.data.mongo.JacksonMongoSessionConverter; import org.springframework.session.data.mongo.MongoIndexedSessionRepository; +import org.springframework.session.data.mongo.MongoSession; import org.springframework.test.util.ReflectionTestUtils; /** @@ -156,6 +159,32 @@ public class MongoHttpSessionConfigurationTest { assertThat(sessionRepository).hasFieldOrPropertyWithValue("maxInactiveIntervalInSeconds", 10000); } + @Test + void customIndexResolverConfigurationWithDefaultMongoSessionConverter() { + + registerAndRefresh(MongoConfiguration.class, CustomIndexResolverConfigurationWithDefaultMongoSessionConverter.class); + + MongoIndexedSessionRepository repository = this.context.getBean(MongoIndexedSessionRepository.class); + IndexResolver indexResolver = this.context.getBean(IndexResolver.class); + + assertThat(repository).isNotNull(); + assertThat(indexResolver).isNotNull(); + assertThat(repository).extracting("mongoSessionConverter").hasFieldOrPropertyWithValue("indexResolver", indexResolver); + } + + @Test + void customIndexResolverConfigurationWithProvidedMongoSessionConverter() { + + registerAndRefresh(MongoConfiguration.class, CustomIndexResolverConfigurationWithProvidedMongoSessionConverter.class); + + MongoIndexedSessionRepository repository = this.context.getBean(MongoIndexedSessionRepository.class); + IndexResolver indexResolver = this.context.getBean(IndexResolver.class); + + assertThat(repository).isNotNull(); + assertThat(indexResolver).isNotNull(); + assertThat(repository).extracting("mongoSessionConverter").hasFieldOrPropertyWithValue("indexResolver", indexResolver); + } + private void registerAndRefresh(Class... annotatedClasses) { this.context.register(annotatedClasses); @@ -264,4 +293,30 @@ public class MongoHttpSessionConfigurationTest { } } + @Configuration + @EnableMongoHttpSession + static class CustomIndexResolverConfigurationWithDefaultMongoSessionConverter { + + @Bean + @SuppressWarnings("unchecked") + public IndexResolver indexResolver() { + return mock(IndexResolver.class); + } + } + + @Configuration + @EnableMongoHttpSession + static class CustomIndexResolverConfigurationWithProvidedMongoSessionConverter { + + @Bean + public AbstractMongoSessionConverter mongoSessionConverter() { + return new JacksonMongoSessionConverter(); + } + + @Bean + @SuppressWarnings("unchecked") + public IndexResolver indexResolver() { + return mock(IndexResolver.class); + } + } } diff --git a/src/test/java/org/springframework/session/data/mongo/config/annotation/web/reactive/ReactiveMongoWebSessionConfigurationTest.java b/src/test/java/org/springframework/session/data/mongo/config/annotation/web/reactive/ReactiveMongoWebSessionConfigurationTest.java index 2aa913d..71ee560 100644 --- a/src/test/java/org/springframework/session/data/mongo/config/annotation/web/reactive/ReactiveMongoWebSessionConfigurationTest.java +++ b/src/test/java/org/springframework/session/data/mongo/config/annotation/web/reactive/ReactiveMongoWebSessionConfigurationTest.java @@ -30,12 +30,14 @@ import org.springframework.context.annotation.Bean; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.ReactiveMongoOperations; import org.springframework.data.mongodb.core.index.IndexOperations; +import org.springframework.session.IndexResolver; import org.springframework.session.ReactiveSessionRepository; import org.springframework.session.config.ReactiveSessionRepositoryCustomizer; import org.springframework.session.config.annotation.web.server.EnableSpringWebSession; import org.springframework.session.data.mongo.AbstractMongoSessionConverter; import org.springframework.session.data.mongo.JacksonMongoSessionConverter; import org.springframework.session.data.mongo.JdkMongoSessionConverter; +import org.springframework.session.data.mongo.MongoSession; import org.springframework.session.data.mongo.ReactiveMongoSessionRepository; import org.springframework.util.ReflectionUtils; import org.springframework.web.server.adapter.WebHttpHandlerBuilder; @@ -177,6 +179,37 @@ public class ReactiveMongoWebSessionConfigurationTest { assertThat(repository).hasFieldOrPropertyWithValue("maxInactiveIntervalInSeconds", 10000); } + @Test + void customIndexResolverConfigurationWithDefaultMongoSessionConverter() { + + this.context = new AnnotationConfigApplicationContext(); + this.context.register(CustomIndexResolverConfigurationWithDefaultMongoSessionConverter.class); + this.context.refresh(); + + ReactiveMongoSessionRepository repository = this.context.getBean(ReactiveMongoSessionRepository.class); + IndexResolver indexResolver = this.context.getBean(IndexResolver.class); + + assertThat(repository).isNotNull(); + assertThat(indexResolver).isNotNull(); + assertThat(repository).extracting("mongoSessionConverter").hasFieldOrPropertyWithValue("indexResolver", indexResolver); + } + + @Test + void customIndexResolverConfigurationWithProvidedMongoSessionConverter() { + + this.context = new AnnotationConfigApplicationContext(); + this.context.register(CustomIndexResolverConfigurationWithProvidedtMongoSessionConverter.class); + this.context.refresh(); + + ReactiveMongoSessionRepository repository = this.context.getBean(ReactiveMongoSessionRepository.class); + IndexResolver indexResolver = this.context.getBean(IndexResolver.class); + + assertThat(repository).isNotNull(); + assertThat(indexResolver).isNotNull(); + assertThat(repository).extracting("mongoSessionConverter").hasFieldOrPropertyWithValue("indexResolver", indexResolver); + } + + /** * Reflectively extract the {@link AbstractMongoSessionConverter} from the {@link ReactiveMongoSessionRepository}. * This is to avoid expanding the surface area of the API. @@ -299,4 +332,42 @@ public class ReactiveMongoWebSessionConfigurationTest { } } + + @EnableMongoWebSession + static class CustomIndexResolverConfigurationWithDefaultMongoSessionConverter { + + @Bean + ReactiveMongoOperations operations() { + return mock(ReactiveMongoOperations.class); + } + + @Bean + @SuppressWarnings("unchecked") + public IndexResolver indexResolver() { + return mock(IndexResolver.class); + } + + } + + @EnableMongoWebSession + static class CustomIndexResolverConfigurationWithProvidedtMongoSessionConverter { + + @Bean + ReactiveMongoOperations operations() { + return mock(ReactiveMongoOperations.class); + } + + @Bean + JacksonMongoSessionConverter jacksonMongoSessionConverter() { + return new JacksonMongoSessionConverter(); + } + + @Bean + @SuppressWarnings("unchecked") + public IndexResolver indexResolver() { + return mock(IndexResolver.class); + } + + + } }