SEC-1229: Further doc and mods to namespace config/naming to make it more consistent
This commit is contained in:
parent
dd3b9553a0
commit
2b89ebdfbb
|
@ -120,7 +120,7 @@ public class FormLoginBeanDefinitionParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sessionStrategy != null) {
|
if (sessionStrategy != null) {
|
||||||
filterBuilder.addPropertyValue("authenticatedSessionStrategy", sessionStrategy);
|
filterBuilder.addPropertyValue("sessionAuthenticationStrategy", sessionStrategy);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StringUtils.hasText(failureHandlerRef)) {
|
if (StringUtils.hasText(failureHandlerRef)) {
|
||||||
|
|
|
@ -41,7 +41,7 @@ import org.springframework.security.web.authentication.SimpleUrlAuthenticationFa
|
||||||
import org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter;
|
import org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter;
|
||||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
|
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
|
||||||
import org.springframework.security.web.session.ConcurrentSessionControlAuthenticatedSessionStrategy;
|
import org.springframework.security.web.session.ConcurrentSessionControlStrategy;
|
||||||
import org.springframework.security.web.session.DefaultSessionAuthenticationStrategy;
|
import org.springframework.security.web.session.DefaultSessionAuthenticationStrategy;
|
||||||
import org.springframework.security.web.session.SessionManagementFilter;
|
import org.springframework.security.web.session.SessionManagementFilter;
|
||||||
import org.springframework.security.web.util.AntUrlPathMatcher;
|
import org.springframework.security.web.util.AntUrlPathMatcher;
|
||||||
|
@ -71,7 +71,8 @@ class HttpConfigurationBuilder {
|
||||||
private static final String OPT_SESSION_FIXATION_MIGRATE_SESSION = "migrateSession";
|
private static final String OPT_SESSION_FIXATION_MIGRATE_SESSION = "migrateSession";
|
||||||
|
|
||||||
private static final String ATT_INVALID_SESSION_URL = "invalid-session-url";
|
private static final String ATT_INVALID_SESSION_URL = "invalid-session-url";
|
||||||
|
private static final String ATT_SESSION_AUTH_STRATEGY_REF = "session-authentication-strategy-ref";
|
||||||
|
private static final String ATT_SESSION_AUTH_ERROR_URL = "session-authentication-error-url";
|
||||||
private static final String ATT_SECURITY_CONTEXT_REPOSITORY = "security-context-repository-ref";
|
private static final String ATT_SECURITY_CONTEXT_REPOSITORY = "security-context-repository-ref";
|
||||||
|
|
||||||
private static final String ATT_DISABLE_URL_REWRITING = "disable-url-rewriting";
|
private static final String ATT_DISABLE_URL_REWRITING = "disable-url-rewriting";
|
||||||
|
@ -197,29 +198,41 @@ class HttpConfigurationBuilder {
|
||||||
|
|
||||||
String sessionFixationAttribute = null;
|
String sessionFixationAttribute = null;
|
||||||
String invalidSessionUrl = null;
|
String invalidSessionUrl = null;
|
||||||
|
String sessionAuthStratRef = null;
|
||||||
|
String errorUrl = null;
|
||||||
|
|
||||||
if (sessionMgmtElt != null) {
|
if (sessionMgmtElt != null) {
|
||||||
sessionFixationAttribute = sessionMgmtElt.getAttribute(ATT_SESSION_FIXATION_PROTECTION);
|
sessionFixationAttribute = sessionMgmtElt.getAttribute(ATT_SESSION_FIXATION_PROTECTION);
|
||||||
invalidSessionUrl = sessionMgmtElt.getAttribute(ATT_INVALID_SESSION_URL);
|
invalidSessionUrl = sessionMgmtElt.getAttribute(ATT_INVALID_SESSION_URL);
|
||||||
|
sessionAuthStratRef = sessionMgmtElt.getAttribute(ATT_SESSION_AUTH_STRATEGY_REF);
|
||||||
|
errorUrl = sessionMgmtElt.getAttribute(ATT_SESSION_AUTH_ERROR_URL);
|
||||||
sessionCtrlElt = DomUtils.getChildElementByTagName(sessionMgmtElt, Elements.CONCURRENT_SESSIONS);
|
sessionCtrlElt = DomUtils.getChildElementByTagName(sessionMgmtElt, Elements.CONCURRENT_SESSIONS);
|
||||||
|
|
||||||
if (sessionCtrlElt != null) {
|
if (sessionCtrlElt != null) {
|
||||||
|
if (StringUtils.hasText(sessionAuthStratRef)) {
|
||||||
|
pc.getReaderContext().error(ATT_SESSION_AUTH_STRATEGY_REF + " attribute cannot be used" +
|
||||||
|
" in combination with <" + Elements.CONCURRENT_SESSIONS + ">", pc.extractSource(sessionCtrlElt));
|
||||||
|
}
|
||||||
createConcurrencyControlFilterAndSessionRegistry(sessionCtrlElt);
|
createConcurrencyControlFilterAndSessionRegistry(sessionCtrlElt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!StringUtils.hasText(sessionFixationAttribute)) {
|
if (!StringUtils.hasText(sessionFixationAttribute)) {
|
||||||
|
if (StringUtils.hasText(sessionAuthStratRef)) {
|
||||||
|
pc.getReaderContext().error(ATT_SESSION_FIXATION_PROTECTION + " attribute cannot be used" +
|
||||||
|
" in combination with " + ATT_SESSION_AUTH_STRATEGY_REF, pc.extractSource(sessionCtrlElt));
|
||||||
|
}
|
||||||
|
|
||||||
sessionFixationAttribute = OPT_SESSION_FIXATION_MIGRATE_SESSION;
|
sessionFixationAttribute = OPT_SESSION_FIXATION_MIGRATE_SESSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean sessionFixationProtectionRequired = !sessionFixationAttribute.equals(OPT_SESSION_FIXATION_NO_PROTECTION);
|
boolean sessionFixationProtectionRequired = !sessionFixationAttribute.equals(OPT_SESSION_FIXATION_NO_PROTECTION);
|
||||||
|
|
||||||
BeanDefinitionBuilder sessionStrategy;
|
BeanDefinitionBuilder sessionStrategy;
|
||||||
String concurrencyErrorUrl = null;
|
|
||||||
|
|
||||||
if (sessionCtrlElt != null) {
|
if (sessionCtrlElt != null) {
|
||||||
assert sessionRegistryRef != null;
|
assert sessionRegistryRef != null;
|
||||||
sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionControlAuthenticatedSessionStrategy.class);
|
sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionControlStrategy.class);
|
||||||
sessionStrategy.addConstructorArgValue(sessionRegistryRef);
|
sessionStrategy.addConstructorArgValue(sessionRegistryRef);
|
||||||
|
|
||||||
String maxSessions = sessionCtrlElt.getAttribute("max-sessions");
|
String maxSessions = sessionCtrlElt.getAttribute("max-sessions");
|
||||||
|
@ -232,10 +245,9 @@ class HttpConfigurationBuilder {
|
||||||
|
|
||||||
if (StringUtils.hasText(exceptionIfMaximumExceeded)) {
|
if (StringUtils.hasText(exceptionIfMaximumExceeded)) {
|
||||||
sessionStrategy.addPropertyValue("exceptionIfMaximumExceeded", exceptionIfMaximumExceeded);
|
sessionStrategy.addPropertyValue("exceptionIfMaximumExceeded", exceptionIfMaximumExceeded);
|
||||||
|
|
||||||
concurrencyErrorUrl = sessionCtrlElt.getAttribute("error-url");
|
|
||||||
}
|
}
|
||||||
} else if (sessionFixationProtectionRequired || StringUtils.hasText(invalidSessionUrl)) {
|
} else if (sessionFixationProtectionRequired || StringUtils.hasText(invalidSessionUrl)
|
||||||
|
|| StringUtils.hasText(sessionAuthStratRef)) {
|
||||||
sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(DefaultSessionAuthenticationStrategy.class);
|
sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(DefaultSessionAuthenticationStrategy.class);
|
||||||
} else {
|
} else {
|
||||||
sfpf = null;
|
sfpf = null;
|
||||||
|
@ -244,28 +256,31 @@ class HttpConfigurationBuilder {
|
||||||
|
|
||||||
BeanDefinitionBuilder sessionMgmtFilter = BeanDefinitionBuilder.rootBeanDefinition(SessionManagementFilter.class);
|
BeanDefinitionBuilder sessionMgmtFilter = BeanDefinitionBuilder.rootBeanDefinition(SessionManagementFilter.class);
|
||||||
RootBeanDefinition failureHandler = new RootBeanDefinition(SimpleUrlAuthenticationFailureHandler.class);
|
RootBeanDefinition failureHandler = new RootBeanDefinition(SimpleUrlAuthenticationFailureHandler.class);
|
||||||
if (StringUtils.hasText(concurrencyErrorUrl)) {
|
if (StringUtils.hasText(errorUrl)) {
|
||||||
failureHandler.getPropertyValues().addPropertyValue("defaultFailureUrl", concurrencyErrorUrl);
|
failureHandler.getPropertyValues().addPropertyValue("defaultFailureUrl", errorUrl);
|
||||||
}
|
}
|
||||||
sessionMgmtFilter.addPropertyValue("authenticationFailureHandler", failureHandler);
|
sessionMgmtFilter.addPropertyValue("authenticationFailureHandler", failureHandler);
|
||||||
sessionMgmtFilter.addConstructorArgValue(contextRepoRef);
|
sessionMgmtFilter.addConstructorArgValue(contextRepoRef);
|
||||||
BeanDefinition strategyBean = sessionStrategy.getBeanDefinition();
|
|
||||||
|
|
||||||
String sessionStrategyId = pc.getReaderContext().registerWithGeneratedName(strategyBean);
|
if (!StringUtils.hasText(sessionAuthStratRef)) {
|
||||||
pc.registerBeanComponent(new BeanComponentDefinition(strategyBean, sessionStrategyId));
|
BeanDefinition strategyBean = sessionStrategy.getBeanDefinition();
|
||||||
sessionMgmtFilter.addPropertyReference("authenticatedSessionStrategy", sessionStrategyId);
|
|
||||||
if (sessionFixationProtectionRequired) {
|
|
||||||
|
|
||||||
sessionStrategy.addPropertyValue("migrateSessionAttributes",
|
if (sessionFixationProtectionRequired) {
|
||||||
Boolean.valueOf(sessionFixationAttribute.equals(OPT_SESSION_FIXATION_MIGRATE_SESSION)));
|
sessionStrategy.addPropertyValue("migrateSessionAttributes",
|
||||||
|
Boolean.valueOf(sessionFixationAttribute.equals(OPT_SESSION_FIXATION_MIGRATE_SESSION)));
|
||||||
|
}
|
||||||
|
sessionAuthStratRef = pc.getReaderContext().registerWithGeneratedName(strategyBean);
|
||||||
|
pc.registerBeanComponent(new BeanComponentDefinition(strategyBean, sessionAuthStratRef));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StringUtils.hasText(invalidSessionUrl)) {
|
if (StringUtils.hasText(invalidSessionUrl)) {
|
||||||
sessionMgmtFilter.addPropertyValue("invalidSessionUrl", invalidSessionUrl);
|
sessionMgmtFilter.addPropertyValue("invalidSessionUrl", invalidSessionUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sessionMgmtFilter.addPropertyReference("sessionAuthenticationStrategy", sessionAuthStratRef);
|
||||||
|
|
||||||
sfpf = (RootBeanDefinition) sessionMgmtFilter.getBeanDefinition();
|
sfpf = (RootBeanDefinition) sessionMgmtFilter.getBeanDefinition();
|
||||||
sessionStrategyRef = new RuntimeBeanReference(sessionStrategyId);
|
sessionStrategyRef = new RuntimeBeanReference(sessionAuthStratRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createConcurrencyControlFilterAndSessionRegistry(Element element) {
|
private void createConcurrencyControlFilterAndSessionRegistry(Element element) {
|
||||||
|
@ -399,9 +414,6 @@ class HttpConfigurationBuilder {
|
||||||
return channelRequestMap;
|
return channelRequestMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void createFilterSecurityInterceptor(BeanReference authManager) {
|
void createFilterSecurityInterceptor(BeanReference authManager) {
|
||||||
BeanDefinitionBuilder fidsBuilder;
|
BeanDefinitionBuilder fidsBuilder;
|
||||||
|
|
||||||
|
|
|
@ -425,6 +425,12 @@ session-management.attlist &=
|
||||||
session-management.attlist &=
|
session-management.attlist &=
|
||||||
## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.
|
## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.
|
||||||
attribute invalid-session-url {xsd:token}?
|
attribute invalid-session-url {xsd:token}?
|
||||||
|
session-management.attlist &=
|
||||||
|
## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter
|
||||||
|
attribute session-authentication-strategy-ref {xsd:token}?
|
||||||
|
session-management.attlist &=
|
||||||
|
## Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (402) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.
|
||||||
|
attribute session-authentication-error-url {xsd:token}?
|
||||||
|
|
||||||
|
|
||||||
concurrency-control =
|
concurrency-control =
|
||||||
|
@ -438,11 +444,8 @@ concurrency-control.attlist &=
|
||||||
## The URL a user will be redirected to if they attempt to use a session which has been "expired" because they have logged in again.
|
## The URL a user will be redirected to if they attempt to use a session which has been "expired" because they have logged in again.
|
||||||
attribute expired-url {xsd:token}?
|
attribute expired-url {xsd:token}?
|
||||||
concurrency-control.attlist &=
|
concurrency-control.attlist &=
|
||||||
## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session.
|
## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.
|
||||||
attribute error-if-maximum-exceeded {boolean}?
|
attribute error-if-maximum-exceeded {boolean}?
|
||||||
concurrency-control.attlist &=
|
|
||||||
## Defines the URL of the error page which should be shown when the maximum is exceeded and error-if-maximum-exceeded is 'true'. If not set, an unauthorized (402) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence.
|
|
||||||
attribute error-url {xsd:token}?
|
|
||||||
concurrency-control.attlist &=
|
concurrency-control.attlist &=
|
||||||
## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.
|
## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration.
|
||||||
attribute session-registry-alias {xsd:token}?
|
attribute session-registry-alias {xsd:token}?
|
||||||
|
|
|
@ -953,6 +953,16 @@
|
||||||
<xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.</xs:documentation>
|
<xs:documentation>The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts.</xs:documentation>
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
|
<xs:attribute name="session-authentication-strategy-ref" type="xs:token">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter</xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
|
<xs:attribute name="session-authentication-error-url" type="xs:token">
|
||||||
|
<xs:annotation>
|
||||||
|
<xs:documentation>Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (402) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence. </xs:documentation>
|
||||||
|
</xs:annotation>
|
||||||
|
</xs:attribute>
|
||||||
</xs:attributeGroup>
|
</xs:attributeGroup>
|
||||||
|
|
||||||
<xs:attributeGroup name="concurrency-control.attlist">
|
<xs:attributeGroup name="concurrency-control.attlist">
|
||||||
|
@ -968,12 +978,7 @@
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
<xs:attribute name="error-if-maximum-exceeded" type="security:boolean">
|
<xs:attribute name="error-if-maximum-exceeded" type="security:boolean">
|
||||||
<xs:annotation>
|
<xs:annotation>
|
||||||
<xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session.</xs:documentation>
|
<xs:documentation>Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL.</xs:documentation>
|
||||||
</xs:annotation>
|
|
||||||
</xs:attribute>
|
|
||||||
<xs:attribute name="error-url" type="xs:token">
|
|
||||||
<xs:annotation>
|
|
||||||
<xs:documentation>Defines the URL of the error page which should be shown when the maximum is exceeded and error-if-maximum-exceeded is 'true'. If not set, an unauthorized (402) error code will be returned to the client. </xs:documentation>
|
|
||||||
</xs:annotation>
|
</xs:annotation>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
<xs:attribute name="session-registry-alias" type="xs:token">
|
<xs:attribute name="session-registry-alias" type="xs:token">
|
||||||
|
|
|
@ -706,22 +706,22 @@ public class HttpSecurityBeanDefinitionParserTests {
|
||||||
public void concurrentSessionMaxSessionsIsCorrectlyConfigured() throws Exception {
|
public void concurrentSessionMaxSessionsIsCorrectlyConfigured() throws Exception {
|
||||||
setContext(
|
setContext(
|
||||||
"<http auto-config='true'>" +
|
"<http auto-config='true'>" +
|
||||||
" <session-management>" +
|
" <session-management session-authentication-error-url='/max-exceeded'>" +
|
||||||
" <concurrency-control max-sessions='2' error-if-maximum-exceeded='true' error-url='/max-exceeded' />" +
|
" <concurrency-control max-sessions='2' error-if-maximum-exceeded='true' />" +
|
||||||
" </session-management>" +
|
" </session-management>" +
|
||||||
"</http>" + AUTH_PROVIDER_XML);
|
"</http>" + AUTH_PROVIDER_XML);
|
||||||
SessionManagementFilter seshStrategy = (SessionManagementFilter) getFilter(SessionManagementFilter.class);
|
SessionManagementFilter seshFilter = (SessionManagementFilter) getFilter(SessionManagementFilter.class);
|
||||||
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("bob", "pass");
|
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("bob", "pass");
|
||||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||||
// Register 2 sessions and then check a third
|
// Register 2 sessions and then check a third
|
||||||
// req.setSession(new MockHttpSession());
|
// req.setSession(new MockHttpSession());
|
||||||
// auth.setDetails(new WebAuthenticationDetails(req));
|
// auth.setDetails(new WebAuthenticationDetails(req));
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
seshStrategy.doFilter(new MockHttpServletRequest(), response, new MockFilterChain());
|
seshFilter.doFilter(new MockHttpServletRequest(), response, new MockFilterChain());
|
||||||
assertNull(response.getRedirectedUrl());
|
assertNull(response.getRedirectedUrl());
|
||||||
seshStrategy.doFilter(new MockHttpServletRequest(), response, new MockFilterChain());
|
seshFilter.doFilter(new MockHttpServletRequest(), response, new MockFilterChain());
|
||||||
assertNull(response.getRedirectedUrl());
|
assertNull(response.getRedirectedUrl());
|
||||||
seshStrategy.doFilter(new MockHttpServletRequest(), response, new MockFilterChain());
|
seshFilter.doFilter(new MockHttpServletRequest(), response, new MockFilterChain());
|
||||||
assertEquals("/max-exceeded", response.getRedirectedUrl());
|
assertEquals("/max-exceeded", response.getRedirectedUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,16 +17,15 @@
|
||||||
<para> The <literal><http></literal> element encapsulates the security configuration for
|
<para> The <literal><http></literal> element encapsulates the security configuration for
|
||||||
the web layer of your application. It creates a <classname>FilterChainProxy</classname> bean
|
the web layer of your application. It creates a <classname>FilterChainProxy</classname> bean
|
||||||
named "springSecurityFilterChain" which maintains the stack of security filters which make up
|
named "springSecurityFilterChain" which maintains the stack of security filters which make up
|
||||||
the web security configuration <footnote>
|
the web security configuration <footnote><para>See the <link xlink:href="#ns-web-xml">
|
||||||
<para>See the <link xlink:href="#ns-web-xml"> introductory chapter</link> for how to set up
|
introductory chapter</link> for how to set up the mapping from your
|
||||||
the mapping from your <literal>web.xml</literal></para>
|
<literal>web.xml</literal></para></footnote>. Some core filters are always created and
|
||||||
</footnote>. Some core filters are always created and others will be added to the stack
|
others will be added to the stack depending on the attributes child elements which are
|
||||||
depending on the attributes child elements which are present. The positions of the standard
|
present. The positions of the standard filters are fixed (see <link xlink:href="#filter-stack"
|
||||||
filters are fixed (see <link xlink:href="#filter-stack">the filter order table</link> in the
|
>the filter order table</link> in the namespace introduction), removing a common source of
|
||||||
namespace introduction), removing a common source of errors with previous versions of the
|
errors with previous versions of the framework when users had to configure the filter chain
|
||||||
framework when users had to configure the filter chain explicitly in
|
explicitly in the<classname>FilterChainProxy</classname> bean. You can, of course, still do
|
||||||
the<classname>FilterChainProxy</classname> bean. You can, of course, still do this if you
|
this if you need full control of the configuration. </para>
|
||||||
need full control of the configuration. </para>
|
|
||||||
<para> All filters which require a reference to the
|
<para> All filters which require a reference to the
|
||||||
<interfacename>AuthenticationManager</interfacename> will be automatically injected with the
|
<interfacename>AuthenticationManager</interfacename> will be automatically injected with the
|
||||||
internal instance created by the namespace configuration (see the <link
|
internal instance created by the namespace configuration (see the <link
|
||||||
|
@ -59,17 +58,6 @@
|
||||||
<para> Whether test URLs should be converted to lower case prior to comparing with defined
|
<para> Whether test URLs should be converted to lower case prior to comparing with defined
|
||||||
path patterns. If unspecified, defaults to "true" </para>
|
path patterns. If unspecified, defaults to "true" </para>
|
||||||
</section>
|
</section>
|
||||||
<section xml:id="session-fixation-protection">
|
|
||||||
<title><literal>session-fixation-protection</literal></title>
|
|
||||||
<para> Indicates whether an existing session should be invalidated when a user authenticates
|
|
||||||
and a new session started. If set to "none" no change will be made. "newSession" will
|
|
||||||
create a new empty session. "migrateSession" will create a new session and copy the
|
|
||||||
session attributes to the new session. Defaults to "migrateSession". </para>
|
|
||||||
<para> If enabled this will add a <classname>SessionFixationProtectionFilter</classname> to
|
|
||||||
the stack. The session fixation protection options on namespace-created instances of
|
|
||||||
<classname>AbstractAuthenticationProcessingFilter</classname> will also be set
|
|
||||||
appropriately. </para>
|
|
||||||
</section>
|
|
||||||
<section xml:id="nsa-realm">
|
<section xml:id="nsa-realm">
|
||||||
<title><literal>realm</literal></title>
|
<title><literal>realm</literal></title>
|
||||||
<para> Sets the realm name used for basic authentication (if enabled). Corresponds to the
|
<para> Sets the realm name used for basic authentication (if enabled). Corresponds to the
|
||||||
|
@ -124,8 +112,9 @@
|
||||||
default <interfacename>AccessDeniedHandler</interfacename> used by the
|
default <interfacename>AccessDeniedHandler</interfacename> used by the
|
||||||
<classname>ExceptionTranslationFilter</classname>, (using the
|
<classname>ExceptionTranslationFilter</classname>, (using the
|
||||||
<literal>error-page</literal> attribute, or to supply your own implementation using the
|
<literal>error-page</literal> attribute, or to supply your own implementation using the
|
||||||
<literal>ref</literal> attribute. This is discussed in more detail in the section on
|
<literal>ref</literal> attribute. This is discussed in more detail in the section on <link
|
||||||
<link xlink:href="#access-denied-handler">the <classname>ExceptionTranslationFilter</classname></link>.</para>
|
xlink:href="#access-denied-handler">the
|
||||||
|
<classname>ExceptionTranslationFilter</classname></link>.</para>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<title>The <literal><intercept-url></literal> Element</title>
|
<title>The <literal><intercept-url></literal> Element</title>
|
||||||
|
@ -161,7 +150,8 @@
|
||||||
there is no preference. If this attribute is present on any
|
there is no preference. If this attribute is present on any
|
||||||
<literal><intercept-url></literal> element, then a
|
<literal><intercept-url></literal> element, then a
|
||||||
<classname>ChannelProcessingFilter</classname> will be added to the filter stack and its
|
<classname>ChannelProcessingFilter</classname> will be added to the filter stack and its
|
||||||
additional dependencies added to the application context. <!--See the chapter on <link
|
additional dependencies added to the application context.
|
||||||
|
<!--See the chapter on <link
|
||||||
xlink:href="#channel-security-config">channel security</link> for an example
|
xlink:href="#channel-security-config">channel security</link> for an example
|
||||||
configuration using traditional beans. --></para>
|
configuration using traditional beans. --></para>
|
||||||
<para> If a <literal><port-mappings></literal> configuration is added, this will be
|
<para> If a <literal><port-mappings></literal> configuration is added, this will be
|
||||||
|
@ -185,14 +175,13 @@
|
||||||
the filter stack and an <classname>LoginUrlAuthenticationEntryPoint</classname> to the
|
the filter stack and an <classname>LoginUrlAuthenticationEntryPoint</classname> to the
|
||||||
application context to provide authentication on demand. This will always take precedence
|
application context to provide authentication on demand. This will always take precedence
|
||||||
over other namespace-created entry points. If no attributes are supplied, a login page will
|
over other namespace-created entry points. If no attributes are supplied, a login page will
|
||||||
be generated automatically at the URL "/spring-security-login" <footnote>
|
be generated automatically at the URL "/spring-security-login" <footnote><para>This feature
|
||||||
<para>This feature is really just provided for convenience and is not intended for
|
is really just provided for convenience and is not intended for production (where a view
|
||||||
production (where a view technology will have been chosen and can be used to render a
|
technology will have been chosen and can be used to render a customized login page). The
|
||||||
customized login page). The class
|
class <classname>DefaultLoginPageGeneratingFilter</classname> is responsible for
|
||||||
<classname>DefaultLoginPageGeneratingFilter</classname> is responsible for rendering
|
rendering the login page and will provide login forms for both normal form login and/or
|
||||||
the login page and will provide login forms for both normal form login and/or OpenID if
|
OpenID if required.</para></footnote> The behaviour can be customized using the
|
||||||
required.</para>
|
following attributes. </para>
|
||||||
</footnote> The behaviour can be customized using the following attributes. </para>
|
|
||||||
<section>
|
<section>
|
||||||
<title><literal>login-page</literal></title>
|
<title><literal>login-page</literal></title>
|
||||||
<para> The URL that should be used to render the login page. Maps to the
|
<para> The URL that should be used to render the login page. Maps to the
|
||||||
|
@ -232,18 +221,18 @@
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<title><literal>authentication-success-handler-ref</literal></title>
|
<title><literal>authentication-success-handler-ref</literal></title>
|
||||||
<para>This can be used as an alternative to <literal>default-target-url</literal>
|
<para>This can be used as an alternative to <literal>default-target-url</literal> and
|
||||||
and <literal>always-use-default-target</literal>, giving you full control over the navigation flow
|
<literal>always-use-default-target</literal>, giving you full control over the
|
||||||
after a successful authentication. The value should be he name of an <interfacename>AuthenticationSuccessHandler</interfacename>
|
navigation flow after a successful authentication. The value should be he name of an
|
||||||
bean in the application context.
|
<interfacename>AuthenticationSuccessHandler</interfacename> bean in the application
|
||||||
</para>
|
context. </para>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<title><literal>authentication-failure-handler-ref</literal></title>
|
<title><literal>authentication-failure-handler-ref</literal></title>
|
||||||
<para>Can be used as an alternative to <literal>authentication-failure-url</literal>, giving you full control over the navigation flow
|
<para>Can be used as an alternative to <literal>authentication-failure-url</literal>, giving
|
||||||
after an authentication failure. The value should be he name of an <interfacename>AuthenticationFailureHandler</interfacename>
|
you full control over the navigation flow after an authentication failure. The value
|
||||||
bean in the application context.
|
should be he name of an <interfacename>AuthenticationFailureHandler</interfacename> bean
|
||||||
</para>
|
in the application context. </para>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
<section xml:id="nsa-http-basic">
|
<section xml:id="nsa-http-basic">
|
||||||
|
@ -286,11 +275,9 @@
|
||||||
<title>The <literal>key</literal> Attribute</title>
|
<title>The <literal>key</literal> Attribute</title>
|
||||||
<para>Maps to the "key" property of <classname>AbstractRememberMeServices</classname>.
|
<para>Maps to the "key" property of <classname>AbstractRememberMeServices</classname>.
|
||||||
Should be set to a unique value to ensure that remember-me cookies are only valid within
|
Should be set to a unique value to ensure that remember-me cookies are only valid within
|
||||||
the one application <footnote>
|
the one application <footnote><para>This doesn't affect the use of
|
||||||
<para>This doesn't affect the use of
|
|
||||||
<classname>PersistentTokenBasedRememberMeServices</classname>, where the tokens are
|
<classname>PersistentTokenBasedRememberMeServices</classname>, where the tokens are
|
||||||
stored on the server side.</para>
|
stored on the server side.</para></footnote>. </para>
|
||||||
</footnote>. </para>
|
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<title><literal>token-validity-seconds</literal></title>
|
<title><literal>token-validity-seconds</literal></title>
|
||||||
|
@ -308,21 +295,33 @@
|
||||||
explicitly using this attribute. </para>
|
explicitly using this attribute. </para>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
<section xml:id="nsa-session-mgmt">
|
||||||
|
<title>The <literal><session-management></literal> Element</title>
|
||||||
|
<para>Session-management related functionality is implemented by the addition of a
|
||||||
|
<classname>SessionManagementFilter</classname> to the filter stack.</para>
|
||||||
|
<section xml:id="session-fixation-protection">
|
||||||
|
<title><literal>session-fixation-protection</literal></title>
|
||||||
|
<para> Indicates whether an existing session should be invalidated when a user authenticates
|
||||||
|
and a new session started. If set to "none" no change will be made. "newSession" will
|
||||||
|
create a new empty session. "migrateSession" will create a new session and copy the
|
||||||
|
session attributes to the new session. Defaults to "migrateSession".</para>
|
||||||
|
<para>
|
||||||
|
If session fixation protection is enabled, the <classname>SessionManagementFilter</classname>
|
||||||
|
is inected with a appropriately configured <classname>DefaultSessionAuthenticationStrategy</classname>.
|
||||||
|
See the Javadoc for this class for more details.
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
<section xml:id="nsa-concurrent-session-control">
|
<section xml:id="nsa-concurrent-session-control">
|
||||||
<title>The <literal><concurrent-session-control></literal> Element</title>
|
<title>The <literal><concurrency-control></literal> Element</title>
|
||||||
<para> Adds support for concurrent session control, allowing limits to be placed on the number
|
<para> Adds support for concurrent session control, allowing limits to be placed on the number
|
||||||
of active sessions a user can have. A <classname>ConcurrentSessionFilter</classname> will be
|
of active sessions a user can have. A <classname>ConcurrentSessionFilter</classname> will be
|
||||||
created, along with a <classname>ConcurrentSessionControllerImpl</classname> and an instance
|
created, and a <classname>ConcurrentSessionControlStrategy</classname> will be used with the
|
||||||
of <interfacename>SessionRegistry</interfacename> (a
|
<classname>SessionManagementFilter</classname>. If a <literal>form-login</literal>
|
||||||
|
element has been declared, the strategy object will also be injected into the created
|
||||||
|
authentication filter. An instance of <interfacename>SessionRegistry</interfacename> (a
|
||||||
<classname>SessionRegistryImpl</classname> instance unless the user wishes to use a custom
|
<classname>SessionRegistryImpl</classname> instance unless the user wishes to use a custom
|
||||||
bean). The controller is registered with the namespace's
|
bean) will be created for use by the strategy.</para>
|
||||||
<interfacename>AuthenticationManager</interfacename>
|
|
||||||
(<classname>ProviderManager</classname>). Other namespace-created beans which require a
|
|
||||||
reference to the <interfacename>SessionRegistry</interfacename> will automatically have it
|
|
||||||
injected. </para>
|
|
||||||
<para> Note that the <literal>forceEagerSessionCreation</literal> of
|
|
||||||
<classname>HttpSessionContextIntegrationFilter</classname> will be set to
|
|
||||||
<literal>true</literal> if concurrent session control is in use. </para>
|
|
||||||
<section>
|
<section>
|
||||||
<title>The <literal>max-sessions</literal> attribute</title>
|
<title>The <literal>max-sessions</literal> attribute</title>
|
||||||
<para>Maps to the <literal>maximumSessions</literal> property of
|
<para>Maps to the <literal>maximumSessions</literal> property of
|
||||||
|
@ -337,10 +336,10 @@
|
||||||
expiry message will just be written directly back to the response. </para>
|
expiry message will just be written directly back to the response. </para>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<title>The <literal>exception-if-maximum-exceeded</literal> attribute</title>
|
<title>The <literal>error-if-maximum-exceeded</literal> attribute</title>
|
||||||
<para>If set to "true" a <exceptionname>ConcurrentLoginException</exceptionname> should be
|
<para>If set to "true" a <exceptionname>SessionAuthenticationException</exceptionname> will
|
||||||
raised when a user attempts to exceed the maximum allowed number of sessions. The default
|
be raised when a user attempts to exceed the maximum allowed number of sessions. The
|
||||||
behaviour is to expire the original session. </para>
|
default behaviour is to expire the original session. </para>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<title>The <literal>session-registry-alias</literal> and
|
<title>The <literal>session-registry-alias</literal> and
|
||||||
|
@ -438,7 +437,8 @@
|
||||||
<section>
|
<section>
|
||||||
<title>The <literal><authentication-provider></literal> Element</title>
|
<title>The <literal><authentication-provider></literal> Element</title>
|
||||||
<para> This element is basically a shorthand syntax for configuring a <link
|
<para> This element is basically a shorthand syntax for configuring a <link
|
||||||
xlink:href="#core-services-dao-provider"><classname>DaoAuthenticationProvider</classname></link>.
|
xlink:href="#core-services-dao-provider"
|
||||||
|
><classname>DaoAuthenticationProvider</classname></link>.
|
||||||
<classname>DaoAuthenticationProvider</classname> loads user information from a
|
<classname>DaoAuthenticationProvider</classname> loads user information from a
|
||||||
<interfacename>UserDetailsService</interfacename> and compares the username/password
|
<interfacename>UserDetailsService</interfacename> and compares the username/password
|
||||||
combination with the values supplied at login. The
|
combination with the values supplied at login. The
|
||||||
|
@ -447,15 +447,15 @@
|
||||||
<literal>user-service-ref</literal> attribute to point to a bean defined elsewhere in
|
<literal>user-service-ref</literal> attribute to point to a bean defined elsewhere in
|
||||||
the application context). You can find examples of these variations in the <link
|
the application context). You can find examples of these variations in the <link
|
||||||
xlink:href="#ns-auth-providers">namespace introduction</link>. </para>
|
xlink:href="#ns-auth-providers">namespace introduction</link>. </para>
|
||||||
<section>
|
<section>
|
||||||
<title>The <literal><password-encoder></literal> Element</title>
|
<title>The <literal><password-encoder></literal> Element</title>
|
||||||
<para>Authentication providers can optionally be configured to use a password encoder as
|
<para>Authentication providers can optionally be configured to use a password encoder as
|
||||||
described in the <link xlink:href="#ns-password-encoder">namespace introduction</link>.
|
described in the <link xlink:href="#ns-password-encoder">namespace introduction</link>.
|
||||||
This will result in the bean being injected with the appropriate <interfacename>PasswordEncoder</interfacename>
|
This will result in the bean being injected with the appropriate
|
||||||
instance, potentially with an accompanying <interfacename>SaltSource</interfacename> bean to
|
<interfacename>PasswordEncoder</interfacename> instance, potentially with an
|
||||||
provide salt values for hashing.
|
accompanying <interfacename>SaltSource</interfacename> bean to provide salt values for
|
||||||
</para>
|
hashing. </para>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<title>Using <literal><authentication-provider></literal> to refer to an
|
<title>Using <literal><authentication-provider></literal> to refer to an
|
||||||
|
@ -497,15 +497,11 @@
|
||||||
<para> Rather than defining security attributes on an individual method or class basis using
|
<para> Rather than defining security attributes on an individual method or class basis using
|
||||||
the <literal>@Secured</literal> annotation, you can define cross-cutting security
|
the <literal>@Secured</literal> annotation, you can define cross-cutting security
|
||||||
constraints across whole sets of methods and interfaces in your service layer using the
|
constraints across whole sets of methods and interfaces in your service layer using the
|
||||||
<literal><protect-pointcut></literal> element. This has two attributes: <itemizedlist>
|
<literal><protect-pointcut></literal> element. This has two attributes:
|
||||||
<listitem>
|
<itemizedlist><listitem><para><literal>expression</literal> - the pointcut
|
||||||
<para><literal>expression</literal> - the pointcut expression</para>
|
expression</para></listitem><listitem><para><literal>access</literal> - the security
|
||||||
</listitem>
|
attributes which apply</para></listitem></itemizedlist> You can find an example in
|
||||||
<listitem>
|
the <link xlink:href="#ns-protect-pointcut">namespace introduction</link>. </para>
|
||||||
<para><literal>access</literal> - the security attributes which apply</para>
|
|
||||||
</listitem>
|
|
||||||
</itemizedlist> You can find an example in the <link xlink:href="#ns-protect-pointcut"
|
|
||||||
>namespace introduction</link>. </para>
|
|
||||||
</section>
|
</section>
|
||||||
<section xml:id="nsa-custom-after-invocation">
|
<section xml:id="nsa-custom-after-invocation">
|
||||||
<title>The <literal><after-invocation-provider></literal> Element</title>
|
<title>The <literal><after-invocation-provider></literal> Element</title>
|
||||||
|
|
|
@ -1,15 +1,65 @@
|
||||||
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="concurrent-sessions"
|
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="session-mgmt"
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
<info>
|
<info>
|
||||||
<title>Session Management</title>
|
<title>Session Management</title>
|
||||||
</info>
|
</info>
|
||||||
<!-- TODO: Expand and refer to namespace options -->
|
<para>HTTP session related functonality is handled by a combination of the
|
||||||
|
<classname>SessionManagementFilter</classname> and the
|
||||||
|
<interfacename>SessionAuthenticationStrategy</interfacename> interface, which the filter
|
||||||
|
delegates to. Typical usage includes session-fixation protection attack prevention, detection of
|
||||||
|
session timeouts and restrictions on how many sessions an authenticated user may have open
|
||||||
|
concurrently.</para>
|
||||||
<section>
|
<section>
|
||||||
<title>SessionManagementFilter</title>
|
<title>SessionManagementFilter</title>
|
||||||
<para>HTTP session related functonality is handled by the
|
<para>The <classname>SessionManagementFilter</classname> checks the contents of the
|
||||||
<classname>SessionManagementFilter</classname>. This </para>
|
<interfacename>SecurityContextRepository</interfacename> against the current contents of the
|
||||||
|
<classname>SecurityContextHolder</classname> to deterine whether a user has been
|
||||||
|
authenticated during the current request, typically by a non-interactive authentication mechanism, such
|
||||||
|
as pre-authentication or remember-me <footnote><para>Authentication by mechanisms which perform a redirect
|
||||||
|
after authenticating (such as form-login) will not be detected by <classname>SessionManagementFilter</classname>,
|
||||||
|
as the filter will not be invoked during the authenticating request. Session-management functionality has to be
|
||||||
|
handled separately in these cases.
|
||||||
|
</para></footnote>.
|
||||||
|
If the repository contains a security context, the
|
||||||
|
filter does nothing. If it doesn't, and the thread-local
|
||||||
|
<interfacename>SecurityContext</interfacename> contains a (non-anonymous)
|
||||||
|
<interfacename>Authentication</interfacename> object, the filter assumes they have been
|
||||||
|
authenticated by a previous filter in the stack. It will then invoke the configured
|
||||||
|
<interfacename>SessionAuthenticationStrategy</interfacename>.</para>
|
||||||
|
<para>If the user is not currently authenticated, the filter will check whether an invalid
|
||||||
|
session ID has been requested (because of a timeout, for example) and will redirect to the
|
||||||
|
configured <literal>invalidSessionUrl</literal> if set. The easiest way to configure this is
|
||||||
|
through the namespace, <link xlink:href="#ns-session-mgmt">as described earlier</link>.</para>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
|
<title><interfacename>SessionAuthenticationStrategy</interfacename></title>
|
||||||
|
<para>
|
||||||
|
<interfacename>SessionAuthenticationStrategy</interfacename> is used by both <classname>SessionManagementFilter</classname>
|
||||||
|
and <classname>AbstractAutheticationProcessingFilter</classname>, so if you are using a customized form-login class, for example, you will need to inject
|
||||||
|
it into both of these. In this case, a typical configuration, combining the namespace and custom beans might look like
|
||||||
|
<programlisting><![CDATA[
|
||||||
|
<http>
|
||||||
|
<custom-filter position="AUTHENTICATION_PROCESSING_FILTER" ref="myAuthFilter" />
|
||||||
|
<session-management session-authentication-strategy-ref="sas"/>
|
||||||
|
</http>
|
||||||
|
|
||||||
|
<beans:bean id="myAuthFilter"
|
||||||
|
class="org.springframework.security.web.authentcation.UsernamePasswordAuthenticationProcessingFilter">
|
||||||
|
<beans:property name="sessionAuthenticationStrategy" ref="sas" />
|
||||||
|
...
|
||||||
|
</beans:bean>
|
||||||
|
|
||||||
|
<beans:bean id="sas"
|
||||||
|
class="org.springframework.security.web.session.DefaultAuthenticatedSessionStrategy">
|
||||||
|
<beans:property name="sessionRegistry" ref="sessionRegistry" />
|
||||||
|
<beans:property name="maximumSessions" value="1" />
|
||||||
|
</beans:bean>
|
||||||
|
]]>
|
||||||
|
</programlisting>
|
||||||
|
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
<section xml:id="concurrent-sessions">
|
||||||
<title>Concurrency Control</title>
|
<title>Concurrency Control</title>
|
||||||
<para>Spring Security is able to prevent a principal from concurrently authenticating to the
|
<para>Spring Security is able to prevent a principal from concurrently authenticating to the
|
||||||
same application more than a specified number of times. Many ISVs take advantage of this to
|
same application more than a specified number of times. Many ISVs take advantage of this to
|
||||||
|
@ -18,14 +68,17 @@
|
||||||
the web application from two different sessions.</para>
|
the web application from two different sessions.</para>
|
||||||
<para>This feature is supported by the namespace, so please check the earlier namespace chapter
|
<para>This feature is supported by the namespace, so please check the earlier namespace chapter
|
||||||
for the simplest configuration. Sometimes you need to customize things though. </para>
|
for the simplest configuration. Sometimes you need to customize things though. </para>
|
||||||
<para>The implementation has changed substantially in Spring Security 3. Previously the
|
<para>The implementation uses a specialized version of
|
||||||
concurrent authentication check was made by the <classname>ProviderManager</classname>, which
|
<interfacename>SessionAuthenticationStrategy</interfacename>, called
|
||||||
could be injected with a <literal>ConcurrentSessionController</literal> which would check if
|
<classname>ConcurrentSessionControlStrategy</classname>.
|
||||||
the user was attempting to exceed the number of sessions permitted. However, this approach
|
<note><para>Previously the concurrent authentication check was made by the
|
||||||
required that an HTTP session be created in advance, which is undesirable. In Spring Security
|
<classname>ProviderManager</classname>, which could be injected with a
|
||||||
3, the user is first authenticated by the <interfacename>AuthenticationManager</interfacename>
|
<literal>ConcurrentSessionController</literal> which would check if the user was
|
||||||
and once they are successfully authenticated, a session is created and the check is made
|
attempting to exceed the number of sessions permitted. However, this approach required that
|
||||||
whether they are allowed to have another session open.</para>
|
an HTTP session be created in advance, which is undesirable. In Spring Security 3, the user
|
||||||
|
is first authenticated by the <interfacename>AuthenticationManager</interfacename> and once
|
||||||
|
they are successfully authenticated, a session is created and the check is made whether they
|
||||||
|
are allowed to have another session open.</para></note></para>
|
||||||
<para>To use concurrent session support, you'll need to add the following to
|
<para>To use concurrent session support, you'll need to add the following to
|
||||||
<literal>web.xml</literal>: <programlisting><![CDATA[
|
<literal>web.xml</literal>: <programlisting><![CDATA[
|
||||||
<listener>
|
<listener>
|
||||||
|
@ -33,40 +86,50 @@
|
||||||
org.springframework.security.web.session.HttpSessionEventPublisher
|
org.springframework.security.web.session.HttpSessionEventPublisher
|
||||||
</listener-class>
|
</listener-class>
|
||||||
</listener> ]]>
|
</listener> ]]>
|
||||||
</programlisting></para>
|
</programlisting></para>
|
||||||
<para>In addition, you will need to add the
|
<para>In addition, you will need to add the
|
||||||
<literal>org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter</literal>
|
<literal>org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter</literal>
|
||||||
to your <classname>FilterChainProxy</classname>. The
|
to your <classname>FilterChainProxy</classname>. The
|
||||||
<classname>ConcurrentSessionFilter</classname> requires two properties,
|
<classname>ConcurrentSessionFilter</classname> requires two properties,
|
||||||
<literal>sessionRegistry</literal>, which generally points to an instance of
|
<literal>sessionRegistry</literal>, which generally points to an instance of
|
||||||
<literal>SessionRegistryImpl</literal>, and <literal>expiredUrl</literal>, which points to
|
<literal>SessionRegistryImpl</literal>, and <literal>expiredUrl</literal>, which points to
|
||||||
the page to display when a session has expired.</para>
|
the page to display when a session has expired. A configuration using the namespace to create the
|
||||||
<para>The <literal>web.xml</literal>
|
<classname>FilterChainProxy</classname> and other default beans might look like this:
|
||||||
<literal>HttpSessionEventPublisher</literal> causes an <literal>ApplicationEvent</literal> to
|
<programlisting><![CDATA[
|
||||||
be published to the Spring <literal>ApplicationContext</literal> every time a
|
<http>
|
||||||
<literal>HttpSession</literal> commences or terminates. This is critical, as it allows the
|
<custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
|
||||||
<classname>SessionRegistryImpl</classname> to be notified when a session ends.</para>
|
<custom-filter position="AUTHENTICATION_PROCESSING_FILTER" ref="myAuthFilter" />
|
||||||
<para>You will also need to wire up the <classname>ConcurrentSessionControllerImpl</classname>
|
|
||||||
and refer to it from your <literal>ProviderManager</literal> bean:</para>
|
<session-management session-authentication-strategy-ref="sas"/>
|
||||||
<para>
|
</http>
|
||||||
<programlisting><![CDATA[
|
|
||||||
<bean id="authenticationManager"
|
<beans:bean id="concurrencyFilter"
|
||||||
class="org.springframework.security.authentication.ProviderManager">
|
class="org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter">
|
||||||
<property name="providers">
|
<beans:property name="sessionRegistry" ref="sessionRegistry" />
|
||||||
<!-- your providers go here -->
|
<beans:property name="expiredUrl" value="/session-expired.htm" />
|
||||||
</property>
|
</beans:bean>
|
||||||
<property name="sessionController" ref="concurrentSessionController"/>
|
|
||||||
</bean>
|
<beans:bean id="myAuthFilter"
|
||||||
|
class="org.springframework.security.web.authentcation.UsernamePasswordAuthenticationProcessingFilter">
|
||||||
<bean id="concurrentSessionController" class=
|
<beans:property name="sessionAuthenticationStrategy" ref="sas" />
|
||||||
"org.springframework.security.authentication.concurrent.ConcurrentSessionControllerImpl">
|
...
|
||||||
<property name="maximumSessions" value="1"/>
|
</beans:bean>
|
||||||
<property name="sessionRegistry">
|
|
||||||
<bean
|
<beans:bean id="sas"
|
||||||
class="org.springframework.security.authentication.concurrent.SessionRegistryImpl"/>
|
class="org.springframework.security.web.session.ConcurrentSessionControlStrategy">
|
||||||
<property>
|
<beans:property name="sessionRegistry" ref="sessionRegistry" />
|
||||||
</bean>
|
<beans:property name="maximumSessions" value="1" />
|
||||||
]]></programlisting>
|
</beans:bean>
|
||||||
|
|
||||||
|
<beans:bean id="sessionRegistry" class="org.springframework.security.authentication.concurrent.SessionRegistryImpl" />
|
||||||
|
]]>
|
||||||
|
</programlisting>
|
||||||
</para>
|
</para>
|
||||||
|
<para>Adding the listener to <filename>web.xml</filename> causes an <literal>ApplicationEvent</literal> to
|
||||||
|
be published to the Spring <literal>ApplicationContext</literal> every time a
|
||||||
|
<literal>HttpSession</literal> commences or terminates. This is critical, as it allows the
|
||||||
|
<classname>SessionRegistryImpl</classname> to be notified when a session ends. Without it, a user
|
||||||
|
will never be able to log back in again once they have exceeded their session allowance, even if they log out
|
||||||
|
of another session or it times out.</para>
|
||||||
</section>
|
</section>
|
||||||
</chapter>
|
</chapter>
|
||||||
|
|
|
@ -355,19 +355,31 @@
|
||||||
in <xref xlink:href="#channel-security"/-->
|
in <xref xlink:href="#channel-security"/-->
|
||||||
</para>
|
</para>
|
||||||
</section>
|
</section>
|
||||||
<section xml:id="ns-concurrent-session">
|
<section xml:id="ns-session-mgmt">
|
||||||
<title>Session Management</title>
|
<title>Session Management</title>
|
||||||
<para> If you wish to place constraints on a single user's ability to log in to your
|
<section>
|
||||||
application, Spring Security supports this out of the box with the following simple
|
<title>Detecting Timeouts</title>
|
||||||
additions. First you need to add the following listener to your <filename>web.xml</filename>
|
<para> You can configure Spring Security to detect the submission of an invalid session ID
|
||||||
file to keep Spring Security updated about session lifecycle events: <programlisting language="xml">
|
and redirect the user to an appropriate URL. This is achieved through the
|
||||||
<![CDATA[
|
<literal>session-management</literal> element:<![CDATA[
|
||||||
|
<http>
|
||||||
|
...
|
||||||
|
<session-management invalid-session-url="/sessionTimeout.htm" />
|
||||||
|
</http>]]></para>
|
||||||
|
</section>
|
||||||
|
<section xml:id="ns-concurrent-sessions">
|
||||||
|
<title>Concurrent Session Control</title>
|
||||||
|
<para>If you wish to place constraints on a single user's ability to log in to your
|
||||||
|
application, Spring Security supports this out of the box with the following simple
|
||||||
|
additions. First you need to add the following listener to your
|
||||||
|
<filename>web.xml</filename> file to keep Spring Security updated about session
|
||||||
|
lifecycle events: <![CDATA[
|
||||||
<listener>
|
<listener>
|
||||||
<listener-class>
|
<listener-class>
|
||||||
org.springframework.security.web.session.HttpSessionEventPublisher
|
org.springframework.security.web.session.HttpSessionEventPublisher
|
||||||
</listener-class>
|
</listener-class>
|
||||||
</listener>
|
</listener>
|
||||||
]]></programlisting> Then add the following lines to your application context: <programlisting language="xml"><![CDATA[
|
]]> Then add the following lines to your application context: <programlisting language="xml"><![CDATA[
|
||||||
<http>
|
<http>
|
||||||
...
|
...
|
||||||
<session-management>
|
<session-management>
|
||||||
|
@ -375,8 +387,8 @@
|
||||||
</session-management>
|
</session-management>
|
||||||
</http>]]>
|
</http>]]>
|
||||||
</programlisting> This will prevent a user from logging in multiple times - a
|
</programlisting> This will prevent a user from logging in multiple times - a
|
||||||
second login will cause the first to be invalidated. Often you would prefer to prevent a
|
second login will cause the first to be invalidated. Often you would prefer to prevent a
|
||||||
second login, in which case you can use <programlisting language="xml"><![CDATA[
|
second login, in which case you can use <programlisting language="xml"><![CDATA[
|
||||||
<http>
|
<http>
|
||||||
...
|
...
|
||||||
<session-management>
|
<session-management>
|
||||||
|
@ -384,13 +396,17 @@
|
||||||
</session-management>
|
</session-management>
|
||||||
</http>]]>
|
</http>]]>
|
||||||
</programlisting> The second login will then be rejected. By
|
</programlisting> The second login will then be rejected. By
|
||||||
<quote>rejected</quote>, we mean that the user will be sent to the
|
<quote>rejected</quote>, we mean that the user will be sent to the
|
||||||
<literal>authentication-failure-url</literal> if form-based login is being used. If the
|
<literal>authentication-failure-url</literal> if form-based login is being used. If the
|
||||||
second authentication takes place through another non-interactive mechanism, such as
|
second authentication takes place through another non-interactive mechanism, such as
|
||||||
<quote>remember-me</quote>, an <quote>unauthorized</quote> (402) error will be sent to the
|
<quote>remember-me</quote>, an <quote>unauthorized</quote> (402) error will be sent to
|
||||||
client. If instead you want to use an error page, you can add the attribute
|
the client. If instead you want to use an error page, you can add the attribute
|
||||||
<literal>error-url</literal> to the <literal>concurrency-control</literal>
|
<literal>session-authentication-error-url</literal> to the
|
||||||
element.<!-- TODO: Link to main section in docs --></para>
|
<literal>session-management</literal> element. </para>
|
||||||
|
<para> If you are using a customized authentication filter for form-based login, then you
|
||||||
|
have to configure concurrent session control support explicitly. More details can be found
|
||||||
|
in the <link xlink:href="#session-mgmt">Session Management chapter</link>. </para>
|
||||||
|
</section>
|
||||||
</section>
|
</section>
|
||||||
<section xml:id="ns-openid">
|
<section xml:id="ns-openid">
|
||||||
<title>OpenID Login</title>
|
<title>OpenID Login</title>
|
||||||
|
|
|
@ -400,7 +400,7 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt
|
||||||
* @param sessionStrategy the implementation to use. If not set a null implementation is
|
* @param sessionStrategy the implementation to use. If not set a null implementation is
|
||||||
* used.
|
* used.
|
||||||
*/
|
*/
|
||||||
public void setAuthenticatedSessionStrategy(SessionAuthenticationStrategy sessionStrategy) {
|
public void setSessionAuthenticationStrategy(SessionAuthenticationStrategy sessionStrategy) {
|
||||||
this.sessionStrategy = sessionStrategy;
|
this.sessionStrategy = sessionStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ import org.springframework.util.Assert;
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class ConcurrentSessionControlAuthenticatedSessionStrategy extends DefaultSessionAuthenticationStrategy
|
public class ConcurrentSessionControlStrategy extends DefaultSessionAuthenticationStrategy
|
||||||
implements MessageSourceAware {
|
implements MessageSourceAware {
|
||||||
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
||||||
private final SessionRegistry sessionRegistry;
|
private final SessionRegistry sessionRegistry;
|
||||||
|
@ -32,7 +32,7 @@ public class ConcurrentSessionControlAuthenticatedSessionStrategy extends Defaul
|
||||||
/**
|
/**
|
||||||
* @param sessionRegistry the session registry which should be updated when the authenticated session is changed.
|
* @param sessionRegistry the session registry which should be updated when the authenticated session is changed.
|
||||||
*/
|
*/
|
||||||
public ConcurrentSessionControlAuthenticatedSessionStrategy(SessionRegistry sessionRegistry) {
|
public ConcurrentSessionControlStrategy(SessionRegistry sessionRegistry) {
|
||||||
Assert.notNull(sessionRegistry, "The sessionRegistry cannot be null");
|
Assert.notNull(sessionRegistry, "The sessionRegistry cannot be null");
|
||||||
super.setAlwaysCreateSession(true);
|
super.setAlwaysCreateSession(true);
|
||||||
this.sessionRegistry = sessionRegistry;
|
this.sessionRegistry = sessionRegistry;
|
|
@ -98,7 +98,7 @@ public class SessionManagementFilter extends GenericFilterBean {
|
||||||
*
|
*
|
||||||
* @param sessionStrategy the strategy object. If not set, a {@link DefaultSessionAuthenticationStrategy} is used.
|
* @param sessionStrategy the strategy object. If not set, a {@link DefaultSessionAuthenticationStrategy} is used.
|
||||||
*/
|
*/
|
||||||
public void setAuthenticatedSessionStrategy(SessionAuthenticationStrategy sessionStrategy) {
|
public void setSessionAuthenticationStrategy(SessionAuthenticationStrategy sessionStrategy) {
|
||||||
Assert.notNull(sessionStrategy, "authenticatedSessionStratedy must not be null");
|
Assert.notNull(sessionStrategy, "authenticatedSessionStratedy must not be null");
|
||||||
this.sessionStrategy = sessionStrategy;
|
this.sessionStrategy = sessionStrategy;
|
||||||
}
|
}
|
||||||
|
|
|
@ -240,7 +240,7 @@ public class AbstractProcessingFilterTests extends TestCase {
|
||||||
MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(true);
|
MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(true);
|
||||||
|
|
||||||
filter.setFilterProcessesUrl("/j_mock_post");
|
filter.setFilterProcessesUrl("/j_mock_post");
|
||||||
filter.setAuthenticatedSessionStrategy(mock(SessionAuthenticationStrategy.class));
|
filter.setSessionAuthenticationStrategy(mock(SessionAuthenticationStrategy.class));
|
||||||
filter.setAuthenticationSuccessHandler(successHandler);
|
filter.setAuthenticationSuccessHandler(successHandler);
|
||||||
filter.setAuthenticationFailureHandler(failureHandler);
|
filter.setAuthenticationFailureHandler(failureHandler);
|
||||||
filter.setAuthenticationManager(mock(AuthenticationManager.class));
|
filter.setAuthenticationManager(mock(AuthenticationManager.class));
|
||||||
|
|
|
@ -48,7 +48,7 @@ public class SessionManagementFilterTests {
|
||||||
// mock that repo contains a security context
|
// mock that repo contains a security context
|
||||||
when(repo.containsContext(any(HttpServletRequest.class))).thenReturn(true);
|
when(repo.containsContext(any(HttpServletRequest.class))).thenReturn(true);
|
||||||
SessionManagementFilter filter = new SessionManagementFilter(repo);
|
SessionManagementFilter filter = new SessionManagementFilter(repo);
|
||||||
filter.setAuthenticatedSessionStrategy(strategy);
|
filter.setSessionAuthenticationStrategy(strategy);
|
||||||
HttpServletRequest request = new MockHttpServletRequest();
|
HttpServletRequest request = new MockHttpServletRequest();
|
||||||
authenticateUser();
|
authenticateUser();
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ public class SessionManagementFilterTests {
|
||||||
SecurityContextRepository repo = mock(SecurityContextRepository.class);
|
SecurityContextRepository repo = mock(SecurityContextRepository.class);
|
||||||
SessionAuthenticationStrategy strategy = mock(SessionAuthenticationStrategy.class);
|
SessionAuthenticationStrategy strategy = mock(SessionAuthenticationStrategy.class);
|
||||||
SessionManagementFilter filter = new SessionManagementFilter(repo);
|
SessionManagementFilter filter = new SessionManagementFilter(repo);
|
||||||
filter.setAuthenticatedSessionStrategy(strategy);
|
filter.setSessionAuthenticationStrategy(strategy);
|
||||||
HttpServletRequest request = new MockHttpServletRequest();
|
HttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
|
||||||
filter.doFilter(request, new MockHttpServletResponse(), new MockFilterChain());
|
filter.doFilter(request, new MockHttpServletResponse(), new MockFilterChain());
|
||||||
|
@ -76,7 +76,7 @@ public class SessionManagementFilterTests {
|
||||||
// repo will return false to containsContext()
|
// repo will return false to containsContext()
|
||||||
SessionAuthenticationStrategy strategy = mock(SessionAuthenticationStrategy.class);
|
SessionAuthenticationStrategy strategy = mock(SessionAuthenticationStrategy.class);
|
||||||
SessionManagementFilter filter = new SessionManagementFilter(repo);
|
SessionManagementFilter filter = new SessionManagementFilter(repo);
|
||||||
filter.setAuthenticatedSessionStrategy(strategy);
|
filter.setSessionAuthenticationStrategy(strategy);
|
||||||
HttpServletRequest request = new MockHttpServletRequest();
|
HttpServletRequest request = new MockHttpServletRequest();
|
||||||
authenticateUser();
|
authenticateUser();
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ public class SessionManagementFilterTests {
|
||||||
// repo will return false to containsContext()
|
// repo will return false to containsContext()
|
||||||
SessionAuthenticationStrategy strategy = mock(SessionAuthenticationStrategy.class);
|
SessionAuthenticationStrategy strategy = mock(SessionAuthenticationStrategy.class);
|
||||||
SessionManagementFilter filter = new SessionManagementFilter(repo);
|
SessionManagementFilter filter = new SessionManagementFilter(repo);
|
||||||
filter.setAuthenticatedSessionStrategy(strategy);
|
filter.setSessionAuthenticationStrategy(strategy);
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
request.setRequestedSessionId("xxx");
|
request.setRequestedSessionId("xxx");
|
||||||
request.setRequestedSessionIdValid(false);
|
request.setRequestedSessionIdValid(false);
|
||||||
|
|
Loading…
Reference in New Issue