Create spring-boot-session modules

This commit is contained in:
Andy Wilkinson
2025-04-03 15:59:55 +01:00
committed by Phillip Webb
parent 014240d576
commit e288c81b7b
69 changed files with 914 additions and 710 deletions

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2012-2020 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.boot.session.autoconfigure;
import org.springframework.session.web.http.DefaultCookieSerializer;
/**
* Callback interface that can be implemented by beans wishing to customize the
* {@link DefaultCookieSerializer} configuration.
*
* @author Vedran Pavic
* @since 4.0.0
*/
@FunctionalInterface
public interface DefaultCookieSerializerCustomizer {
/**
* Customize the cookie serializer.
* @param cookieSerializer the {@code DefaultCookieSerializer} to customize
*/
void customize(DefaultCookieSerializer cookieSerializer);
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright 2012-2025 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.boot.session.autoconfigure;
import java.time.Duration;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.boot.web.server.Cookie;
import org.springframework.boot.web.server.Cookie.SameSite;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.web.authentication.RememberMeServices;
import org.springframework.session.Session;
import org.springframework.session.security.web.authentication.SpringSessionRememberMeServices;
import org.springframework.session.web.http.CookieHttpSessionIdResolver;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;
import org.springframework.session.web.http.HttpSessionIdResolver;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Spring Session.
*
* @author Andy Wilkinson
* @author Tommy Ludwig
* @author Eddú Meléndez
* @author Stephane Nicoll
* @author Vedran Pavic
* @author Weix Sun
* @since 4.0.0
*/
@AutoConfiguration
@ConditionalOnClass(Session.class)
@ConditionalOnWebApplication
@EnableConfigurationProperties({ ServerProperties.class, SessionProperties.class })
public class SessionAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@Import(SessionRepositoryFilterConfiguration.class)
static class ServletSessionConfiguration {
@Bean
@Conditional(DefaultCookieSerializerCondition.class)
DefaultCookieSerializer cookieSerializer(ServerProperties serverProperties,
ObjectProvider<DefaultCookieSerializerCustomizer> cookieSerializerCustomizers) {
Cookie cookie = serverProperties.getServlet().getSession().getCookie();
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(cookie::getName).to(cookieSerializer::setCookieName);
map.from(cookie::getDomain).to(cookieSerializer::setDomainName);
map.from(cookie::getPath).to(cookieSerializer::setCookiePath);
map.from(cookie::getHttpOnly).to(cookieSerializer::setUseHttpOnlyCookie);
map.from(cookie::getSecure).to(cookieSerializer::setUseSecureCookie);
map.from(cookie::getMaxAge).asInt(Duration::getSeconds).to(cookieSerializer::setCookieMaxAge);
map.from(cookie::getSameSite).as(SameSite::attributeValue).to(cookieSerializer::setSameSite);
map.from(cookie::getPartitioned).to(cookieSerializer::setPartitioned);
cookieSerializerCustomizers.orderedStream().forEach((customizer) -> customizer.customize(cookieSerializer));
return cookieSerializer;
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RememberMeServices.class)
static class RememberMeServicesConfiguration {
@Bean
DefaultCookieSerializerCustomizer rememberMeServicesCookieSerializerCustomizer() {
return (cookieSerializer) -> cookieSerializer
.setRememberMeRequestAttribute(SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR);
}
}
}
/**
* Condition to trigger the creation of a {@link DefaultCookieSerializer}. This kicks
* in if either no {@link HttpSessionIdResolver} and {@link CookieSerializer} beans
* are registered, or if {@link CookieHttpSessionIdResolver} is registered but
* {@link CookieSerializer} is not.
*/
static class DefaultCookieSerializerCondition extends AnyNestedCondition {
DefaultCookieSerializerCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnMissingBean({ HttpSessionIdResolver.class, CookieSerializer.class })
static class NoComponentsAvailable {
}
@ConditionalOnBean(CookieHttpSessionIdResolver.class)
@ConditionalOnMissingBean(CookieSerializer.class)
static class CookieHttpSessionIdResolverAvailable {
}
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2012-2025 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.boot.session.autoconfigure;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Supplier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.convert.DurationUnit;
import org.springframework.boot.web.servlet.DispatcherType;
import org.springframework.session.web.http.SessionRepositoryFilter;
/**
* Configuration properties for Spring Session.
*
* @author Tommy Ludwig
* @author Stephane Nicoll
* @author Vedran Pavic
* @since 4.0.0
*/
@ConfigurationProperties("spring.session")
public class SessionProperties {
/**
* Session timeout. If a duration suffix is not specified, seconds will be used.
*/
@DurationUnit(ChronoUnit.SECONDS)
private Duration timeout;
private Servlet servlet = new Servlet();
public Duration getTimeout() {
return this.timeout;
}
public void setTimeout(Duration timeout) {
this.timeout = timeout;
}
public Servlet getServlet() {
return this.servlet;
}
public void setServlet(Servlet servlet) {
this.servlet = servlet;
}
/**
* Determine the session timeout. If no timeout is configured, the
* {@code fallbackTimeout} is used.
* @param fallbackTimeout a fallback timeout value if the timeout isn't configured
* @return the session timeout
*/
public Duration determineTimeout(Supplier<Duration> fallbackTimeout) {
return (this.timeout != null) ? this.timeout : fallbackTimeout.get();
}
/**
* Servlet-related properties.
*/
public static class Servlet {
/**
* Session repository filter order.
*/
private int filterOrder = SessionRepositoryFilter.DEFAULT_ORDER;
/**
* Session repository filter dispatcher types.
*/
private Set<DispatcherType> filterDispatcherTypes = new HashSet<>(
Arrays.asList(DispatcherType.ASYNC, DispatcherType.ERROR, DispatcherType.REQUEST));
public int getFilterOrder() {
return this.filterOrder;
}
public void setFilterOrder(int filterOrder) {
this.filterOrder = filterOrder;
}
public Set<DispatcherType> getFilterDispatcherTypes() {
return this.filterDispatcherTypes;
}
public void setFilterDispatcherTypes(Set<DispatcherType> filterDispatcherTypes) {
this.filterDispatcherTypes = filterDispatcherTypes;
}
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2012-2023 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.boot.session.autoconfigure;
import java.util.EnumSet;
import java.util.stream.Collectors;
import jakarta.servlet.DispatcherType;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.web.http.SessionRepositoryFilter;
import org.springframework.util.Assert;
/**
* Configuration for customizing the registration of the {@link SessionRepositoryFilter}.
*
* @author Andy Wilkinson
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(SessionRepositoryFilter.class)
@EnableConfigurationProperties(SessionProperties.class)
class SessionRepositoryFilterConfiguration {
@Bean
DelegatingFilterProxyRegistrationBean sessionRepositoryFilterRegistration(SessionProperties sessionProperties,
ListableBeanFactory beanFactory) {
String[] targetBeanNames = beanFactory.getBeanNamesForType(SessionRepositoryFilter.class, false, false);
Assert.state(targetBeanNames.length == 1, "Expected single SessionRepositoryFilter bean");
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
targetBeanNames[0]);
registration.setDispatcherTypes(getDispatcherTypes(sessionProperties));
registration.setOrder(sessionProperties.getServlet().getFilterOrder());
return registration;
}
private EnumSet<DispatcherType> getDispatcherTypes(SessionProperties sessionProperties) {
SessionProperties.Servlet servletProperties = sessionProperties.getServlet();
if (servletProperties.getFilterDispatcherTypes() == null) {
return null;
}
return servletProperties.getFilterDispatcherTypes()
.stream()
.map((type) -> DispatcherType.valueOf(type.name()))
.collect(Collectors.toCollection(() -> EnumSet.noneOf(DispatcherType.class)));
}
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2012-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.
*/
/**
* Auto-configuration for Spring Session.
*/
package org.springframework.boot.session.autoconfigure;

View File

@@ -0,0 +1,12 @@
{
"properties": [
{
"name": "spring.session.servlet.filter-dispatcher-types",
"defaultValue": [
"async",
"error",
"request"
]
}
]
}

View File

@@ -0,0 +1 @@
org.springframework.boot.session.autoconfigure.SessionAutoConfiguration