Add session configuration properties

Add support for the following server properties which can be used to
configure the session:

	server.session.tracking-modes
	server.session.cookie.name
	server.session.cookie.domain
	server.session.cookie.path
	server.session.cookie.comment
	server.session.cookie.http-only
	server.session.cookie.secure
	server.session.cookie.max-age

In addition `server.session-timeout` is now deprecated and has been
replaced with `server.session.timeout`.

Fixes gh-3240
This commit is contained in:
Phillip Webb 2015-07-08 13:44:41 -07:00
parent 5938c967a3
commit 3588ca8637
5 changed files with 294 additions and 14 deletions

View File

@ -61,10 +61,9 @@ public class SessionAutoConfiguration {
@PostConstruct
public void applyConfigurationProperties() {
if (this.serverProperties.getSessionTimeout() != null) {
this.sessionRepository
.setDefaultMaxInactiveInterval(this.serverProperties
.getSessionTimeout());
Integer timeout = this.serverProperties.getSession().getTimeout();
if (timeout != null) {
this.sessionRepository.setDefaultMaxInactiveInterval(timeout);
}
}

View File

@ -21,7 +21,12 @@ import java.net.InetAddress;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.SessionCookieConfig;
import javax.servlet.SessionTrackingMode;
import javax.validation.constraints.NotNull;
import org.apache.catalina.Context;
@ -31,6 +36,7 @@ import org.apache.catalina.valves.RemoteIpValve;
import org.apache.coyote.AbstractProtocol;
import org.apache.coyote.ProtocolHandler;
import org.apache.coyote.http11.AbstractHttp11Protocol;
import org.springframework.boot.autoconfigure.web.ServerProperties.Session.Cookie;
import org.springframework.boot.context.embedded.Compression;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
@ -38,6 +44,7 @@ import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomi
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.InitParameterConfiguringServletContextInitializer;
import org.springframework.boot.context.embedded.JspServlet;
import org.springframework.boot.context.embedded.ServletContextInitializer;
import org.springframework.boot.context.embedded.Ssl;
import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer;
@ -72,11 +79,6 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord
*/
private InetAddress address;
/**
* Session timeout in seconds.
*/
private Integer sessionTimeout;
/**
* Context path of the application.
*/
@ -87,6 +89,8 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord
*/
private String displayName = "application";
private Session session = new Session();
@NestedConfigurationProperty
private Ssl ssl;
@ -192,12 +196,28 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord
this.address = address;
}
/**
* Set the session timeout
* @return the session timeout
* @deprecated since 1.3.0 in favor of {@code session.timeout}.
*/
@Deprecated
public Integer getSessionTimeout() {
return this.sessionTimeout;
return this.session.getTimeout();
}
/**
* Get the session timeout
* @param sessionTimeout the session timeout
* @deprecated since 1.3.0 in favor of {@code session.timeout}.
*/
@Deprecated
public void setSessionTimeout(Integer sessionTimeout) {
this.sessionTimeout = sessionTimeout;
this.session.setTimeout(sessionTimeout);
}
public Session getSession() {
return this.session;
}
public Ssl getSsl() {
@ -238,8 +258,8 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord
if (getDisplayName() != null) {
container.setDisplayName(getDisplayName());
}
if (getSessionTimeout() != null) {
container.setSessionTimeout(getSessionTimeout());
if (getSession().getTimeout() != null) {
container.setSessionTimeout(getSession().getTimeout());
}
if (getSsl() != null) {
container.setSsl(getSsl());
@ -258,6 +278,7 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord
getUndertow().customizeUndertow(
(UndertowEmbeddedServletContainerFactory) container);
}
container.addInitializers(new SessionConfiguringInitializer(this.session));
container.addInitializers(new InitParameterConfiguringServletContextInitializer(
getContextParameters()));
}
@ -288,6 +309,137 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord
return prefix + path;
}
public static class Session {
/**
* Session timeout in seconds.
*/
private Integer timeout;
/**
* Session tracking modes (one or more of the following: "cookie", "url", "ssl")
*/
private Set<SessionTrackingMode> trackingModes;
private Cookie cookie = new Cookie();
public Cookie getCookie() {
return this.cookie;
}
public Integer getTimeout() {
return this.timeout;
}
public void setTimeout(Integer sessionTimeout) {
this.timeout = sessionTimeout;
}
public Set<SessionTrackingMode> getTrackingModes() {
return this.trackingModes;
}
public void setTrackingModes(Set<SessionTrackingMode> trackingModes) {
this.trackingModes = trackingModes;
}
public static class Cookie {
/**
* Session cookie name.
*/
private String name;
/**
* Domain for the session cookie.
*/
private String domain;
/**
* Path of the session cookie.
*/
private String path;
/**
* Comment for the session cookie.
*/
private String comment;
/**
* "HttpOnly" flag for the session cookie.
*/
private Boolean httpOnly;
/**
* "Secure" flag for the session cookie.
*/
private Boolean secure;
/**
* Maximum age of the session cookie in seconds.
*/
private Integer maxAge;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getDomain() {
return this.domain;
}
public void setDomain(String domain) {
this.domain = domain;
}
public String getPath() {
return this.path;
}
public void setPath(String path) {
this.path = path;
}
public String getComment() {
return this.comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public Boolean getHttpOnly() {
return this.httpOnly;
}
public void setHttpOnly(Boolean httpOnly) {
this.httpOnly = httpOnly;
}
public Boolean getSecure() {
return this.secure;
}
public void setSecure(Boolean secure) {
this.secure = secure;
}
public Integer getMaxAge() {
return this.maxAge;
}
public void setMaxAge(Integer maxAge) {
this.maxAge = maxAge;
}
}
}
public static class Tomcat {
/**
@ -662,4 +814,51 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord
}
/**
* {@link ServletContextInitializer} to apply appropriate parts of the {@link Session}
* configuration.
*/
private static class SessionConfiguringInitializer implements
ServletContextInitializer {
private final Session session;
public SessionConfiguringInitializer(Session session) {
this.session = session;
}
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
if (this.session.getTrackingModes() != null) {
servletContext.setSessionTrackingModes(this.session.getTrackingModes());
}
configureSessionCookie(servletContext.getSessionCookieConfig());
}
private void configureSessionCookie(SessionCookieConfig config) {
Cookie cookie = this.session.getCookie();
if (cookie.getName() != null) {
config.setName(cookie.getName());
}
if (cookie.getDomain() != null) {
config.setDomain(cookie.getDomain());
}
if (cookie.getPath() != null) {
config.setPath(cookie.getPath());
}
if (cookie.getComment() != null) {
config.setComment(cookie.getComment());
}
if (cookie.getHttpOnly() != null) {
config.setHttpOnly(cookie.getHttpOnly());
}
if (cookie.getSecure() != null) {
config.setSecure(cookie.getSecure());
}
if (cookie.getMaxAge() != null) {
config.setMaxAge(cookie.getMaxAge());
}
}
}
}

View File

@ -157,6 +157,12 @@
"type": "java.lang.String",
"description": "Spring MVC view suffix.",
"deprecated": true
},
{
"name": "server.session-timeout",
"type": "java.lang.Integer",
"description": "Session timeout in seconds.",
"deprecated": true
}
],"hints": [
{

View File

@ -18,21 +18,34 @@ package org.springframework.boot.autoconfigure.web;
import java.net.InetAddress;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.SessionCookieConfig;
import javax.servlet.SessionTrackingMode;
import org.apache.catalina.Valve;
import org.apache.catalina.valves.RemoteIpValve;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.boot.bind.RelaxedDataBinder;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.ServletContextInitializer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@ -48,6 +61,14 @@ public class ServerPropertiesTests {
private final ServerProperties properties = new ServerProperties();
@Captor
private ArgumentCaptor<ServletContextInitializer[]> initializersCaptor;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testAddressBinding() throws Exception {
RelaxedDataBinder binder = new RelaxedDataBinder(this.properties, "server");
@ -123,6 +144,53 @@ public class ServerPropertiesTests {
verify(factory).setDisplayName("TestName");
}
@Test
public void customizeSessionProperties() throws Exception {
Map<String, String> map = new HashMap<String, String>();
map.put("server.session.timeout", "123");
map.put("server.session.tracking-modes", "cookie,url");
map.put("server.session.cookie.name", "testname");
map.put("server.session.cookie.domain", "testdomain");
map.put("server.session.cookie.path", "/testpath");
map.put("server.session.cookie.comment", "testcomment");
map.put("server.session.cookie.http-only", "true");
map.put("server.session.cookie.secure", "true");
map.put("server.session.cookie.max-age", "60");
bindProperties(map);
ConfigurableEmbeddedServletContainer factory = mock(ConfigurableEmbeddedServletContainer.class);
ServletContext servletContext = mock(ServletContext.class);
SessionCookieConfig sessionCookieConfig = mock(SessionCookieConfig.class);
given(servletContext.getSessionCookieConfig()).willReturn(sessionCookieConfig);
this.properties.customize(factory);
triggerInitializers(factory, servletContext);
verify(factory).setSessionTimeout(123);
verify(servletContext).setSessionTrackingModes(
EnumSet.of(SessionTrackingMode.COOKIE, SessionTrackingMode.URL));
verify(sessionCookieConfig).setName("testname");
verify(sessionCookieConfig).setDomain("testdomain");
verify(sessionCookieConfig).setPath("/testpath");
verify(sessionCookieConfig).setComment("testcomment");
verify(sessionCookieConfig).setHttpOnly(true);
verify(sessionCookieConfig).setSecure(true);
verify(sessionCookieConfig).setMaxAge(60);
}
private void triggerInitializers(ConfigurableEmbeddedServletContainer container,
ServletContext servletContext) throws ServletException {
verify(container, atLeastOnce()).addInitializers(
this.initializersCaptor.capture());
for (Object initializers : this.initializersCaptor.getAllValues()) {
if (initializers instanceof ServletContextInitializer) {
((ServletContextInitializer) initializers).onStartup(servletContext);
}
else {
for (ServletContextInitializer initializer : (ServletContextInitializer[]) initializers) {
initializer.onStartup(servletContext);
}
}
}
}
@Test
public void testCustomizeTomcatPort() throws Exception {
ConfigurableEmbeddedServletContainer factory = mock(ConfigurableEmbeddedServletContainer.class);

View File

@ -65,7 +65,6 @@ content into your application; rather pick only the properties that you need.
# EMBEDDED SERVER CONFIGURATION ({sc-spring-boot-autoconfigure}/web/ServerProperties.{sc-ext}[ServerProperties])
server.port=8080
server.address= # bind to a specific NIC
server.session-timeout= # session timeout in seconds
server.compression.enabled=false # if response compression is enabled
server.compression.mime-types=text/html,text/xml,text/plain,text/css # comma-separated list of MIME types that should be compressed
server.compression.min-response-size=2048 # minimum response size that is required for compression to be performed
@ -76,6 +75,15 @@ content into your application; rather pick only the properties that you need.
server.jsp-servlet.registered=true # Whether or not the JSP servlet is registered
server.servlet-path= # the servlet path, defaults to '/'
server.display-name= # the display name of the application
server.session.timeout= # session timeout in seconds
server.session.tracking-modes= # tracking modes (one or more of "cookie" ,"url", "ssl")
server.session.cookie.name=
server.session.cookie.domain=
server.session.cookie.path=
server.session.cookie.comment=
server.session.cookie.http-only=
server.session.cookie.secure=
server.session.cookie.max-age=
server.ssl.enabled=true # if SSL support is enabled
server.ssl.client-auth= # want or need
server.ssl.key-alias=