spring-projects / spring-session

Spring Session
https://spring.io/projects/spring-session
Apache License 2.0
1.86k stars 1.12k forks source link

Introduce a configuration property in order to disable session management for testing or other purposes #2459

Closed tonny1983 closed 1 year ago

tonny1983 commented 1 year ago

Expected Behavior

A new configuration property, maybe spring.session.enabled, might be introduced in order to disable the session management funtion in some circumstances.

Current Behavior There seems be no such configuration properties which could disable the session management fucntion.

Context Before version 3.0, according to the documenet there was a property, spring.session.store-type, which could be set to none in order to disable it.

Meanwhile, from version 3.0, according to the issue and document, the property has been removed because the persistence implementation order was defined when multiple were available.

Actually, there might be some situations, especially in testing, in which the users prefer to disable the session management, because it is disigned for the use case where clustered sessions are required, but for some business logic testing in a single node.

There may be an alternative solution. The spring-session-jdbc dependency can be introduced in testing environment and then uses @EnableJdbcHttpSession annotation to configurate a JDBC connection to an in-memory (might be H2) which often occurs in testing. Therefore, in production environment, the applcation may connect to redis cluster for high performance and, in testing, connect to in-memory DB for the requirement of spring session.

However, the alternative might not work for reactive application due to only Redis and MongoDB are supported.

marcusdacoregio commented 1 year ago

Hi, @tonny1983. Properties is a feature from Spring Boot, so unfortunately there is nothing we can do related to that on Spring Session side.

If you want Spring Boot to back off and you want to have control over Spring Session configuration, you can use @Enable*HttpSession as mentioned in Spring Boot docs:

You can take control over Spring Session’s configuration using @Enable*HttpSession (servlet) or @Enable*WebSession (reactive).
This will cause the auto-configuration to back off.
Spring Session can then be configured using the annotation’s attributes rather than the previously described configuration properties.

You can use the annotation in combination with @ConditionalOnProperty annotation from Spring Boot.

Another solution is to use the Testcontainers support or Docker Compose support so you have the same setup in both dev/prod.

petervanwylen commented 4 months ago

But is there some sort of shortcut to fully disable Spring Session based on the application properties file? The original question was about whether there was a way to actually disable it entirely instead of take over the manual configuration. Why would I annotate with @Enable*HttpSession if I don't want it to run at all? For example, if I want to revert to regular Tomcat HttpSession when developing locally, I shouldn't annotate @EnableRedisHttpSession or any of the @Enable* methods right? Or is there a way that I can use @EnableRedisHttpSession and functionally make it not use Redis at all?

wasd0109 commented 3 months ago

Having the same question and issue, from my current understanding Spring session now go through a list of implementation and check if its in classpath and auto config if it detected a implementation and the only way to explicitly specify which store type to use is with @Enable*HttpSession. The issue is that currently with our codebases this results in two behaviour, 1. specify using Redis store type by using @EnableRedisHttpSession, or 2. not specifying it and since the redis implementation is in the classpath, forced to use Redis store type Unless there is a @DisableHttpSession or similar annotation I can no longer replicate the behaviour of spring.session.store-type=none without removing the redis implementation from my classpath or to outright implement my own SessionRepository.

petervanwylen commented 3 months ago

Is there a way that I can selectively at run time disable spring session entirely? Almost as if it weren't in pom.xml at all? For situations where redis isn't available? Some devs don't run redis locally and I'd prefer not to make them install it as part of their dev environment if they can revert back to regular HttpSession when the spring profile selected is for local development.

lasat commented 3 months ago

Hi there, my solution for this problem was this setup:

@SpringBootApplication
@EnableAutoConfiguration(exclude = { RedisAutoConfiguration.class})
public class Application {
/**
 * clone of private class:
 * org.springframework.boot.autoconfigure.session.RedisSessionConfiguration
 */
@Configuration
@ConditionalOnProperty(prefix = "application", name = "session.store", havingValue = "redis")
@EnableConfigurationProperties(RedisSessionProperties.class)
@Import({ RedisAutoConfiguration.class })
public class RedisSessionConfig {

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnProperty(prefix = "spring.session.redis", name = "repository-type", havingValue = "default", matchIfMissing = true)
    @Import(RedisHttpSessionConfiguration.class)
    @EnableRedisHttpSession
    class DefaultRedisSessionConfiguration {

        @Bean
        @Order(Ordered.HIGHEST_PRECEDENCE)
        SessionRepositoryCustomizer<RedisSessionRepository> springBootSessionRepositoryCustomizer(
                SessionProperties sessionProperties, RedisSessionProperties redisSessionProperties,
                ServerProperties serverProperties) {
            String cleanupCron = redisSessionProperties.getCleanupCron();
            if (cleanupCron != null) {
                throw new InvalidConfigurationPropertyValueException("spring.session.redis.cleanup-cron", cleanupCron,
                        "Cron-based cleanup is only supported when "
                                + "spring.session.redis.repository-type is set to indexed.");
            }
            return (sessionRepository) -> {
                PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
                map.from(sessionProperties
                        .determineTimeout(() -> serverProperties.getServlet().getSession().getTimeout()))
                        .to(sessionRepository::setDefaultMaxInactiveInterval);
                map.from(redisSessionProperties::getNamespace).to(sessionRepository::setRedisKeyNamespace);
                map.from(redisSessionProperties::getFlushMode).to(sessionRepository::setFlushMode);
                map.from(redisSessionProperties::getSaveMode).to(sessionRepository::setSaveMode);
            };
        }

    }

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnProperty(prefix = "spring.session.redis", name = "repository-type", havingValue = "indexed")
    @Import(RedisIndexedHttpSessionConfiguration.class)
    @EnableRedisIndexedHttpSession
    class IndexedRedisSessionConfiguration {

        @Bean
        @ConditionalOnMissingBean
        ConfigureRedisAction configureRedisAction(RedisSessionProperties redisSessionProperties) {
            return switch (redisSessionProperties.getConfigureAction()) {
            case NOTIFY_KEYSPACE_EVENTS -> new ConfigureNotifyKeyspaceEventsAction();
            case NONE -> ConfigureRedisAction.NO_OP;
            };
        }

        @Bean
        @Order(Ordered.HIGHEST_PRECEDENCE)
        SessionRepositoryCustomizer<RedisIndexedSessionRepository> springBootSessionRepositoryCustomizer(
                SessionProperties sessionProperties, RedisSessionProperties redisSessionProperties,
                ServerProperties serverProperties) {
            return (sessionRepository) -> {
                PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
                map.from(sessionProperties
                        .determineTimeout(() -> serverProperties.getServlet().getSession().getTimeout()))
                        .to(sessionRepository::setDefaultMaxInactiveInterval);
                map.from(redisSessionProperties::getNamespace).to(sessionRepository::setRedisKeyNamespace);
                map.from(redisSessionProperties::getFlushMode).to(sessionRepository::setFlushMode);
                map.from(redisSessionProperties::getSaveMode).to(sessionRepository::setSaveMode);
                map.from(redisSessionProperties::getCleanupCron).to(sessionRepository::setCleanupCron);
            };
        }

    }
}

Afterwards I was able to enable RedisSession Store with: application.session.store = redis. Configuration of redis as usual.

# Sessions
##########
# *|redis
#application.session.store = redis
# *|indexed
spring.session.redis.repository-type = indexed
spring.session.redis.namespace = redis:sessions
spring.data.redis.host = localhost
spring.data.redis.password =
spring.data.redis.port = 6379

Hope this help you.