From a6f6042831dab37af9dbd3dec7c06da175be851a Mon Sep 17 00:00:00 2001 From: Vedran Pavic Date: Tue, 30 Apr 2019 21:37:00 +0200 Subject: [PATCH] Introduce IndexResolver This commit introduces IndexResolver as a strategy interface for resolving index values in FindByIndexNameSessionRepository implementations. Resolves: #557 --- .../session/DelegatingIndexResolver.java | 54 +++++++++++++++ .../session/IndexResolver.java | 39 +++++++++++ .../session/PrincipalNameIndexResolver.java | 54 +++++++++++++++ .../session/SingleIndexResolver.java | 52 +++++++++++++++ .../session/DelegatingIndexResolverTests.java | 66 +++++++++++++++++++ .../PrincipalNameIndexResolverTests.java | 66 +++++++++++++++++++ .../RedisOperationsSessionRepository.java | 42 ++++-------- ...RedisOperationsSessionRepositoryTests.java | 32 --------- .../hazelcast/HazelcastSessionRepository.java | 35 +++------- .../jdbc/JdbcOperationsSessionRepository.java | 46 ++++--------- 10 files changed, 364 insertions(+), 122 deletions(-) create mode 100644 spring-session-core/src/main/java/org/springframework/session/DelegatingIndexResolver.java create mode 100644 spring-session-core/src/main/java/org/springframework/session/IndexResolver.java create mode 100644 spring-session-core/src/main/java/org/springframework/session/PrincipalNameIndexResolver.java create mode 100644 spring-session-core/src/main/java/org/springframework/session/SingleIndexResolver.java create mode 100644 spring-session-core/src/test/java/org/springframework/session/DelegatingIndexResolverTests.java create mode 100644 spring-session-core/src/test/java/org/springframework/session/PrincipalNameIndexResolverTests.java diff --git a/spring-session-core/src/main/java/org/springframework/session/DelegatingIndexResolver.java b/spring-session-core/src/main/java/org/springframework/session/DelegatingIndexResolver.java new file mode 100644 index 00000000..2417736f --- /dev/null +++ b/spring-session-core/src/main/java/org/springframework/session/DelegatingIndexResolver.java @@ -0,0 +1,54 @@ +/* + * Copyright 2014-2019 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; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * An {@link IndexResolver} that resolves indexes using multiple @{link IndexResolver} + * delegates. + * + * @param the type of Session being handled + * @author Vedran Pavic + * @since 2.2.0 + */ +public class DelegatingIndexResolver implements IndexResolver { + + private final List> delegates; + + public DelegatingIndexResolver(List> delegates) { + this.delegates = Collections.unmodifiableList(delegates); + } + + @SafeVarargs + public DelegatingIndexResolver(IndexResolver... delegates) { + this(Arrays.asList(delegates)); + } + + public Map resolveIndexesFor(S session) { + Map indexes = new HashMap<>(); + for (IndexResolver delegate : this.delegates) { + indexes.putAll(delegate.resolveIndexesFor(session)); + } + return indexes; + } + +} diff --git a/spring-session-core/src/main/java/org/springframework/session/IndexResolver.java b/spring-session-core/src/main/java/org/springframework/session/IndexResolver.java new file mode 100644 index 00000000..587808f0 --- /dev/null +++ b/spring-session-core/src/main/java/org/springframework/session/IndexResolver.java @@ -0,0 +1,39 @@ +/* + * Copyright 2014-2019 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; + +import java.util.Map; + +/** + * Strategy interface for resolving the {@link Session}'s indexes. + * + * @param the type of Session being handled + * @author Rob Winch + * @author Vedran Pavic + * @since 2.2.0 + * @see FindByIndexNameSessionRepository + */ +public interface IndexResolver { + + /** + * Resolve indexes for the session. + * @param session the session + * @return a map of resolved indexes, never {@code null} + */ + Map resolveIndexesFor(S session); + +} diff --git a/spring-session-core/src/main/java/org/springframework/session/PrincipalNameIndexResolver.java b/spring-session-core/src/main/java/org/springframework/session/PrincipalNameIndexResolver.java new file mode 100644 index 00000000..868aae50 --- /dev/null +++ b/spring-session-core/src/main/java/org/springframework/session/PrincipalNameIndexResolver.java @@ -0,0 +1,54 @@ +/* + * Copyright 2014-2019 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; + +import org.springframework.expression.Expression; +import org.springframework.expression.spel.standard.SpelExpressionParser; + +/** + * {@link IndexResolver} to resolve the principal name from session attribute named + * {@link FindByIndexNameSessionRepository#PRINCIPAL_NAME_INDEX_NAME} or Spring Security + * context stored in the session under {@code SPRING_SECURITY_CONTEXT} attribute. + * + * @param the type of Session being handled + * @author Vedran Pavic + * @since 2.2.0 + */ +public class PrincipalNameIndexResolver extends SingleIndexResolver { + + private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT"; + + private static final SpelExpressionParser parser = new SpelExpressionParser(); + + public PrincipalNameIndexResolver() { + super(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME); + } + + public String resolveIndexValueFor(S session) { + String principalName = session.getAttribute(getIndexName()); + if (principalName != null) { + return principalName; + } + Object authentication = session.getAttribute(SPRING_SECURITY_CONTEXT); + if (authentication != null) { + Expression expression = parser.parseExpression("authentication?.name"); + return expression.getValue(authentication, String.class); + } + return null; + } + +} diff --git a/spring-session-core/src/main/java/org/springframework/session/SingleIndexResolver.java b/spring-session-core/src/main/java/org/springframework/session/SingleIndexResolver.java new file mode 100644 index 00000000..1d6e81a3 --- /dev/null +++ b/spring-session-core/src/main/java/org/springframework/session/SingleIndexResolver.java @@ -0,0 +1,52 @@ +/* + * Copyright 2014-2019 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; + +import java.util.Collections; +import java.util.Map; + +import org.springframework.util.Assert; + +/** + * Base class for {@link IndexResolver}s that resolve a single index. + * + * @param the type of Session being handled + * @author Rob Winch + * @author Vedran Pavic + * @since 2.2.0 + */ +public abstract class SingleIndexResolver implements IndexResolver { + + private final String indexName; + + protected SingleIndexResolver(String indexName) { + Assert.notNull(indexName, "Index name must not be null"); + this.indexName = indexName; + } + + protected String getIndexName() { + return this.indexName; + } + + public abstract String resolveIndexValueFor(S session); + + public final Map resolveIndexesFor(S session) { + String indexValue = resolveIndexValueFor(session); + return (indexValue != null) ? Collections.singletonMap(this.indexName, indexValue) : Collections.emptyMap(); + } + +} diff --git a/spring-session-core/src/test/java/org/springframework/session/DelegatingIndexResolverTests.java b/spring-session-core/src/test/java/org/springframework/session/DelegatingIndexResolverTests.java new file mode 100644 index 00000000..55cb7cee --- /dev/null +++ b/spring-session-core/src/test/java/org/springframework/session/DelegatingIndexResolverTests.java @@ -0,0 +1,66 @@ +/* + * Copyright 2014-2019 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; + +import java.util.Collections; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link DelegatingIndexResolver}. + * + * @author Vedran Pavic + */ +class DelegatingIndexResolverTests { + + private DelegatingIndexResolver indexResolver; + + @BeforeEach + void setUp() { + this.indexResolver = new DelegatingIndexResolver<>(new TestIndexResolver("one"), new TestIndexResolver("two")); + } + + @Test + void resolve() { + MapSession session = new MapSession(); + session.setAttribute("one", "first"); + session.setAttribute("two", "second"); + Map indexes = this.indexResolver.resolveIndexesFor(session); + assertThat(indexes).hasSize(2); + assertThat(indexes.get("one")).isEqualTo("first"); + assertThat(indexes.get("two")).isEqualTo("second"); + } + + private static class TestIndexResolver implements IndexResolver { + + private final String supportedIndex; + + TestIndexResolver(String supportedIndex) { + this.supportedIndex = supportedIndex; + } + + public Map resolveIndexesFor(MapSession session) { + return Collections.singletonMap(this.supportedIndex, session.getAttribute(this.supportedIndex)); + } + + } + +} diff --git a/spring-session-core/src/test/java/org/springframework/session/PrincipalNameIndexResolverTests.java b/spring-session-core/src/test/java/org/springframework/session/PrincipalNameIndexResolverTests.java new file mode 100644 index 00000000..aec43d1c --- /dev/null +++ b/spring-session-core/src/test/java/org/springframework/session/PrincipalNameIndexResolverTests.java @@ -0,0 +1,66 @@ +/* + * Copyright 2014-2019 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; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextImpl; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link PrincipalNameIndexResolver}. + * + * @author Vedran Pavic + */ +class PrincipalNameIndexResolverTests { + + private static final String PRINCIPAL_NAME = "principalName"; + + private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT"; + + private PrincipalNameIndexResolver indexResolver; + + @BeforeEach + void setUp() { + this.indexResolver = new PrincipalNameIndexResolver<>(); + } + + @Test + void resolveFromPrincipalName() { + MapSession session = new MapSession(); + session.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, PRINCIPAL_NAME); + assertThat(this.indexResolver.resolveIndexValueFor(session)).isEqualTo(PRINCIPAL_NAME); + } + + @Test + void resolveFromSpringSecurityContext() { + Authentication authentication = new UsernamePasswordAuthenticationToken(PRINCIPAL_NAME, "notused", + AuthorityUtils.createAuthorityList("ROLE_USER")); + SecurityContext context = new SecurityContextImpl(); + context.setAuthentication(authentication); + MapSession session = new MapSession(); + session.setAttribute(SPRING_SECURITY_CONTEXT, context); + assertThat(this.indexResolver.resolveIndexValueFor(session)).isEqualTo(PRINCIPAL_NAME); + } + +} diff --git a/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisOperationsSessionRepository.java b/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisOperationsSessionRepository.java index 49cd8601..8b3c7867 100644 --- a/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisOperationsSessionRepository.java +++ b/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisOperationsSessionRepository.java @@ -36,10 +36,11 @@ import org.springframework.data.redis.core.BoundHashOperations; import org.springframework.data.redis.core.RedisOperations; import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer; -import org.springframework.expression.Expression; -import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.session.DelegatingIndexResolver; import org.springframework.session.FindByIndexNameSessionRepository; +import org.springframework.session.IndexResolver; import org.springframework.session.MapSession; +import org.springframework.session.PrincipalNameIndexResolver; import org.springframework.session.Session; import org.springframework.session.events.SessionCreatedEvent; import org.springframework.session.events.SessionDeletedEvent; @@ -252,8 +253,6 @@ public class RedisOperationsSessionRepository private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT"; - static PrincipalNameResolver PRINCIPAL_NAME_RESOLVER = new PrincipalNameResolver(); - /** * The default Redis database used by Spring Session. */ @@ -281,6 +280,8 @@ public class RedisOperationsSessionRepository private final RedisSessionExpirationPolicy expirationPolicy; + private final IndexResolver indexResolver; + private ApplicationEventPublisher eventPublisher = new ApplicationEventPublisher() { @Override public void publishEvent(ApplicationEvent event) { @@ -311,6 +312,7 @@ public class RedisOperationsSessionRepository this.sessionRedisOperations = sessionRedisOperations; this.expirationPolicy = new RedisSessionExpirationPolicy(sessionRedisOperations, this::getExpirationsKey, this::getSessionKey); + this.indexResolver = new DelegatingIndexResolver<>(new PrincipalNameIndexResolver<>()); configureSessionChannels(); } @@ -533,7 +535,8 @@ public class RedisOperationsSessionRepository private void cleanupPrincipalIndex(RedisSession session) { String sessionId = session.getId(); - String principal = PRINCIPAL_NAME_RESOLVER.resolvePrincipal(session); + Map indexes = RedisOperationsSessionRepository.this.indexResolver.resolveIndexesFor(session); + String principal = indexes.get(PRINCIPAL_NAME_INDEX_NAME); if (principal != null) { this.sessionRedisOperations.boundSetOps(getPrincipalKey(principal)).remove(sessionId); } @@ -689,8 +692,9 @@ public class RedisOperationsSessionRepository RedisSession(MapSession cached) { Assert.notNull(cached, "MapSession cannot be null"); this.cached = cached; - this.originalPrincipalName = PRINCIPAL_NAME_RESOLVER.resolvePrincipal(this); this.originalSessionId = cached.getId(); + Map indexes = RedisOperationsSessionRepository.this.indexResolver.resolveIndexesFor(this); + this.originalPrincipalName = indexes.get(PRINCIPAL_NAME_INDEX_NAME); } public void setNew(boolean isNew) { @@ -800,7 +804,9 @@ public class RedisOperationsSessionRepository RedisOperationsSessionRepository.this.sessionRedisOperations.boundSetOps(originalPrincipalRedisKey) .remove(sessionId); } - String principal = PRINCIPAL_NAME_RESOLVER.resolvePrincipal(this); + Map indexes = RedisOperationsSessionRepository.this.indexResolver + .resolveIndexesFor(this); + String principal = indexes.get(PRINCIPAL_NAME_INDEX_NAME); this.originalPrincipalName = principal; if (principal != null) { String principalRedisKey = getPrincipalKey(principal); @@ -851,26 +857,4 @@ public class RedisOperationsSessionRepository } - /** - * Principal name resolver helper class. - */ - static class PrincipalNameResolver { - - private SpelExpressionParser parser = new SpelExpressionParser(); - - public String resolvePrincipal(Session session) { - String principalName = session.getAttribute(PRINCIPAL_NAME_INDEX_NAME); - if (principalName != null) { - return principalName; - } - Object authentication = session.getAttribute(SPRING_SECURITY_CONTEXT); - if (authentication != null) { - Expression expression = this.parser.parseExpression("authentication?.name"); - return expression.getValue(authentication, String.class); - } - return null; - } - - } - } diff --git a/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisOperationsSessionRepositoryTests.java b/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisOperationsSessionRepositoryTests.java index c695e289..ce90ce6f 100644 --- a/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisOperationsSessionRepositoryTests.java +++ b/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisOperationsSessionRepositoryTests.java @@ -45,15 +45,9 @@ import org.springframework.data.redis.core.BoundValueOperations; import org.springframework.data.redis.core.RedisOperations; import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextImpl; import org.springframework.session.FindByIndexNameSessionRepository; import org.springframework.session.MapSession; import org.springframework.session.Session; -import org.springframework.session.data.redis.RedisOperationsSessionRepository.PrincipalNameResolver; import org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession; import org.springframework.session.events.AbstractSessionEvent; @@ -597,32 +591,6 @@ class RedisOperationsSessionRepositoryTests { verifyZeroInteractions(this.boundHashOperations); } - @Test - void resolvePrincipalIndex() { - PrincipalNameResolver resolver = RedisOperationsSessionRepository.PRINCIPAL_NAME_RESOLVER; - String username = "username"; - RedisSession session = this.redisRepository.createSession(); - session.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, username); - - assertThat(resolver.resolvePrincipal(session)).isEqualTo(username); - } - - @Test - void resolveIndexOnSecurityContext() { - String principal = "resolveIndexOnSecurityContext"; - Authentication authentication = new UsernamePasswordAuthenticationToken(principal, "notused", - AuthorityUtils.createAuthorityList("ROLE_USER")); - SecurityContext context = new SecurityContextImpl(); - context.setAuthentication(authentication); - - PrincipalNameResolver resolver = RedisOperationsSessionRepository.PRINCIPAL_NAME_RESOLVER; - - RedisSession session = this.redisRepository.createSession(); - session.setAttribute(SPRING_SECURITY_CONTEXT_KEY, context); - - assertThat(resolver.resolvePrincipal(session)).isEqualTo(principal); - } - @Test void flushModeOnSaveCreate() { this.redisRepository.createSession(); diff --git a/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastSessionRepository.java b/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastSessionRepository.java index 498c1903..71a5e33d 100644 --- a/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastSessionRepository.java +++ b/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastSessionRepository.java @@ -40,10 +40,11 @@ import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEventPublisher; -import org.springframework.expression.Expression; -import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.session.DelegatingIndexResolver; import org.springframework.session.FindByIndexNameSessionRepository; +import org.springframework.session.IndexResolver; import org.springframework.session.MapSession; +import org.springframework.session.PrincipalNameIndexResolver; import org.springframework.session.Session; import org.springframework.session.events.AbstractSessionEvent; import org.springframework.session.events.SessionCreatedEvent; @@ -129,10 +130,10 @@ public class HazelcastSessionRepository private static final Log logger = LogFactory.getLog(HazelcastSessionRepository.class); - private static final PrincipalNameResolver principalNameResolver = new PrincipalNameResolver(); - private final HazelcastInstance hazelcastInstance; + private final IndexResolver indexResolver; + private ApplicationEventPublisher eventPublisher = new ApplicationEventPublisher() { @Override @@ -162,6 +163,7 @@ public class HazelcastSessionRepository public HazelcastSessionRepository(HazelcastInstance hazelcastInstance) { Assert.notNull(hazelcastInstance, "HazelcastInstance must not be null"); this.hazelcastInstance = hazelcastInstance; + this.indexResolver = new DelegatingIndexResolver<>(new PrincipalNameIndexResolver<>()); } @PostConstruct @@ -425,7 +427,8 @@ public class HazelcastSessionRepository this.delegate.setAttribute(attributeName, attributeValue); this.delta.put(attributeName, attributeValue); if (SPRING_SECURITY_CONTEXT.equals(attributeName)) { - String principal = (attributeValue != null) ? principalNameResolver.resolvePrincipal(this) : null; + Map indexes = HazelcastSessionRepository.this.indexResolver.resolveIndexesFor(this); + String principal = (attributeValue != null) ? indexes.get(PRINCIPAL_NAME_INDEX_NAME) : null; this.delegate.setAttribute(PRINCIPAL_NAME_INDEX_NAME, principal); } flushImmediateIfNecessary(); @@ -460,26 +463,4 @@ public class HazelcastSessionRepository } - /** - * Resolves the Spring Security principal name. - */ - static class PrincipalNameResolver { - - private SpelExpressionParser parser = new SpelExpressionParser(); - - public String resolvePrincipal(Session session) { - String principalName = session.getAttribute(PRINCIPAL_NAME_INDEX_NAME); - if (principalName != null) { - return principalName; - } - Object authentication = session.getAttribute(SPRING_SECURITY_CONTEXT); - if (authentication != null) { - Expression expression = this.parser.parseExpression("authentication?.name"); - return expression.getValue(authentication, String.class); - } - return null; - } - - } - } diff --git a/spring-session-jdbc/src/main/java/org/springframework/session/jdbc/JdbcOperationsSessionRepository.java b/spring-session-jdbc/src/main/java/org/springframework/session/jdbc/JdbcOperationsSessionRepository.java index d6dcee3c..e915f7e2 100644 --- a/spring-session-jdbc/src/main/java/org/springframework/session/jdbc/JdbcOperationsSessionRepository.java +++ b/spring-session-jdbc/src/main/java/org/springframework/session/jdbc/JdbcOperationsSessionRepository.java @@ -40,16 +40,17 @@ import org.springframework.core.convert.support.GenericConversionService; import org.springframework.core.serializer.support.DeserializingConverter; import org.springframework.core.serializer.support.SerializingConverter; import org.springframework.dao.DataAccessException; -import org.springframework.expression.Expression; -import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.ResultSetExtractor; import org.springframework.jdbc.support.lob.DefaultLobHandler; import org.springframework.jdbc.support.lob.LobHandler; +import org.springframework.session.DelegatingIndexResolver; import org.springframework.session.FindByIndexNameSessionRepository; +import org.springframework.session.IndexResolver; import org.springframework.session.MapSession; +import org.springframework.session.PrincipalNameIndexResolver; import org.springframework.session.Session; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; @@ -197,12 +198,12 @@ public class JdbcOperationsSessionRepository private static final Log logger = LogFactory.getLog(JdbcOperationsSessionRepository.class); - private static final PrincipalNameResolver PRINCIPAL_NAME_RESOLVER = new PrincipalNameResolver(); - private final JdbcOperations jdbcOperations; private final ResultSetExtractor> extractor = new SessionResultSetExtractor(); + private final IndexResolver indexResolver; + private TransactionOperations transactionOperations = new TransactionOperations() { @Override @@ -271,6 +272,7 @@ public class JdbcOperationsSessionRepository public JdbcOperationsSessionRepository(JdbcOperations jdbcOperations) { Assert.notNull(jdbcOperations, "JdbcOperations must not be null"); this.jdbcOperations = jdbcOperations; + this.indexResolver = new DelegatingIndexResolver<>(new PrincipalNameIndexResolver<>()); this.conversionService = createDefaultConversionService(); prepareQueries(); } @@ -406,6 +408,8 @@ public class JdbcOperationsSessionRepository @Override protected void doInTransactionWithoutResult(TransactionStatus status) { + Map indexes = JdbcOperationsSessionRepository.this.indexResolver + .resolveIndexesFor(session); JdbcOperationsSessionRepository.this.jdbcOperations .update(JdbcOperationsSessionRepository.this.createSessionQuery, (ps) -> { ps.setString(1, session.primaryKey); @@ -414,7 +418,7 @@ public class JdbcOperationsSessionRepository ps.setLong(4, session.getLastAccessedTime().toEpochMilli()); ps.setInt(5, (int) session.getMaxInactiveInterval().getSeconds()); ps.setLong(6, session.getExpiryTime().toEpochMilli()); - ps.setString(7, session.getPrincipalName()); + ps.setString(7, indexes.get(PRINCIPAL_NAME_INDEX_NAME)); }); Set attributeNames = session.getAttributeNames(); if (!attributeNames.isEmpty()) { @@ -430,13 +434,15 @@ public class JdbcOperationsSessionRepository @Override protected void doInTransactionWithoutResult(TransactionStatus status) { if (session.isChanged()) { + Map indexes = JdbcOperationsSessionRepository.this.indexResolver + .resolveIndexesFor(session); JdbcOperationsSessionRepository.this.jdbcOperations .update(JdbcOperationsSessionRepository.this.updateSessionQuery, (ps) -> { ps.setString(1, session.getId()); ps.setLong(2, session.getLastAccessedTime().toEpochMilli()); ps.setInt(3, (int) session.getMaxInactiveInterval().getSeconds()); ps.setLong(4, session.getExpiryTime().toEpochMilli()); - ps.setString(5, session.getPrincipalName()); + ps.setString(5, indexes.get(PRINCIPAL_NAME_INDEX_NAME)); ps.setString(6, session.primaryKey); }); } @@ -742,10 +748,6 @@ public class JdbcOperationsSessionRepository this.delta.clear(); } - String getPrincipalName() { - return PRINCIPAL_NAME_RESOLVER.resolvePrincipal(this); - } - Instant getExpiryTime() { return getLastAccessedTime().plus(getMaxInactiveInterval()); } @@ -838,30 +840,6 @@ public class JdbcOperationsSessionRepository } - /** - * Resolves the Spring Security principal name. - * - * @author Vedran Pavic - */ - static class PrincipalNameResolver { - - private SpelExpressionParser parser = new SpelExpressionParser(); - - public String resolvePrincipal(Session session) { - String principalName = session.getAttribute(PRINCIPAL_NAME_INDEX_NAME); - if (principalName != null) { - return principalName; - } - Object authentication = session.getAttribute(SPRING_SECURITY_CONTEXT); - if (authentication != null) { - Expression expression = this.parser.parseExpression("authentication?.name"); - return expression.getValue(authentication, String.class); - } - return null; - } - - } - private class SessionResultSetExtractor implements ResultSetExtractor> { @Override