SEC-1133: Support for setting of authenticationDetailsSource property for form-login, openid-login, http-basic and x509 namespace elements. These elements now support an additional 'authentication-details-source-ref' attribute.

This commit is contained in:
Luke Taylor 2010-08-14 15:10:03 +01:00
parent 992566b6cb
commit 4bd41cbf72
9 changed files with 96 additions and 33 deletions

View File

@ -16,7 +16,7 @@ dependencies {
provided "javax.servlet:servlet-api:2.5"
groovy group: 'org.codehaus.groovy', name: 'groovy', version: '1.7.3'
groovy group: 'org.codehaus.groovy', name: 'groovy', version: '1.7.4'
testCompile project(':spring-security-ldap'),
project(':spring-security-openid'),

View File

@ -64,6 +64,8 @@ final class AuthenticationConfigBuilder {
private static final String OPEN_ID_ATTRIBUTE_FACTORY_CLASS = "org.springframework.security.openid.RegexBasedAxFetchListFactory";
static final String AUTHENTICATION_PROCESSING_FILTER_CLASS = "org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter";
static final String ATT_AUTH_DETAILS_SOURCE_REF = "authentication-details-source-ref";
private static final String ATT_AUTO_CONFIG = "auto-config";
private static final String ATT_ACCESS_DENIED_PAGE = "access-denied-page";
@ -72,6 +74,7 @@ final class AuthenticationConfigBuilder {
private static final String ATT_USER_SERVICE_REF = "user-service-ref";
private final Element httpElt;
private final ParserContext pc;
@ -148,7 +151,7 @@ final class AuthenticationConfigBuilder {
key = DEF_KEY;
}
rememberMeFilter = (RootBeanDefinition) new RememberMeBeanDefinitionParser(key).parse(rememberMeElt, pc);
rememberMeFilter = new RememberMeBeanDefinitionParser(key).parse(rememberMeElt, pc);
rememberMeFilter.getPropertyValues().addPropertyValue("authenticationManager", authenticationManager);
rememberMeServicesId = ((RuntimeBeanReference) rememberMeFilter.getPropertyValues().getPropertyValue("rememberMeServices").getValue()).getBeanName();
createRememberMeProvider(key);
@ -291,35 +294,41 @@ final class AuthenticationConfigBuilder {
void createBasicFilter(BeanReference authManager) {
Element basicAuthElt = DomUtils.getChildElementByTagName(httpElt, Elements.BASIC_AUTH);
if (basicAuthElt == null && !autoConfig) {
// No basic auth, do nothing
return;
}
String realm = httpElt.getAttribute(ATT_REALM);
if (!StringUtils.hasText(realm)) {
realm = DEF_REALM;
}
RootBeanDefinition filter = null;
BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.rootBeanDefinition(BasicAuthenticationFilter.class);
if (basicAuthElt != null || autoConfig) {
BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.rootBeanDefinition(BasicAuthenticationFilter.class);
String entryPointId;
String entryPointId;
if (basicAuthElt != null && StringUtils.hasText(basicAuthElt.getAttribute(ATT_ENTRY_POINT_REF))) {
if (basicAuthElt != null) {
if (StringUtils.hasText(basicAuthElt.getAttribute(ATT_ENTRY_POINT_REF))) {
basicEntryPoint = new RuntimeBeanReference(basicAuthElt.getAttribute(ATT_ENTRY_POINT_REF));
} else {
RootBeanDefinition entryPoint = new RootBeanDefinition(BasicAuthenticationEntryPoint.class);
entryPoint.setSource(pc.extractSource(httpElt));
entryPoint.getPropertyValues().addPropertyValue("realmName", realm);
entryPointId = pc.getReaderContext().generateBeanName(entryPoint);
pc.registerBeanComponent(new BeanComponentDefinition(entryPoint, entryPointId));
basicEntryPoint = new RuntimeBeanReference(entryPointId);
}
filterBuilder.addPropertyValue("authenticationManager", authManager);
filterBuilder.addPropertyValue("authenticationEntryPoint", basicEntryPoint);
filter = (RootBeanDefinition) filterBuilder.getBeanDefinition();
injectAuthenticationDetailsSource(basicAuthElt, filterBuilder);
}
basicFilter = filter;
if (basicEntryPoint == null) {
RootBeanDefinition entryPoint = new RootBeanDefinition(BasicAuthenticationEntryPoint.class);
entryPoint.setSource(pc.extractSource(httpElt));
entryPoint.getPropertyValues().addPropertyValue("realmName", realm);
entryPointId = pc.getReaderContext().generateBeanName(entryPoint);
pc.registerBeanComponent(new BeanComponentDefinition(entryPoint, entryPointId));
basicEntryPoint = new RuntimeBeanReference(entryPointId);
}
filterBuilder.addPropertyValue("authenticationManager", authManager);
filterBuilder.addPropertyValue("authenticationEntryPoint", basicEntryPoint);
basicFilter = filterBuilder.getBeanDefinition();
}
void createX509Filter(BeanReference authManager) {
@ -339,6 +348,9 @@ final class AuthenticationConfigBuilder {
filterBuilder.addPropertyValue("principalExtractor", extractor.getBeanDefinition());
}
injectAuthenticationDetailsSource(x509Elt, filterBuilder);
filter = (RootBeanDefinition) filterBuilder.getBeanDefinition();
createPrauthEntryPoint(x509Elt);
@ -348,6 +360,14 @@ final class AuthenticationConfigBuilder {
x509Filter = filter;
}
private void injectAuthenticationDetailsSource(Element elt, BeanDefinitionBuilder filterBuilder) {
String authDetailsSourceRef = elt.getAttribute(AuthenticationConfigBuilder.ATT_AUTH_DETAILS_SOURCE_REF);
if (StringUtils.hasText(authDetailsSourceRef)) {
filterBuilder.addPropertyReference("authenticationDetailsSource", authDetailsSourceRef);
}
}
private void createX509Provider() {
Element x509Elt = DomUtils.getChildElementByTagName(httpElt, Elements.X509);
BeanDefinition provider = new RootBeanDefinition(PreAuthenticatedAuthenticationProvider.class);

View File

@ -68,6 +68,7 @@ public class FormLoginBeanDefinitionParser {
// Only available with form-login
String usernameParameter = null;
String passwordParameter = null;
String authDetailsSourceRef = null;
Object source = null;
@ -83,6 +84,8 @@ public class FormLoginBeanDefinitionParser {
loginPage = elt.getAttribute(ATT_LOGIN_PAGE);
successHandlerRef = elt.getAttribute(ATT_SUCCESS_HANDLER_REF);
failureHandlerRef = elt.getAttribute(ATT_FAILURE_HANDLER_REF);
authDetailsSourceRef = elt.getAttribute(AuthenticationConfigBuilder.ATT_AUTH_DETAILS_SOURCE_REF);
if (!StringUtils.hasText(loginPage)) {
loginPage = null;
@ -93,7 +96,7 @@ public class FormLoginBeanDefinitionParser {
}
filterBean = createFilterBean(loginUrl, defaultTargetUrl, alwaysUseDefault, loginPage, authenticationFailureUrl,
successHandlerRef, failureHandlerRef);
successHandlerRef, failureHandlerRef, authDetailsSourceRef);
if (StringUtils.hasText(usernameParameter)) {
filterBean.getPropertyValues().addPropertyValue("usernameParameter", usernameParameter);
@ -114,7 +117,8 @@ public class FormLoginBeanDefinitionParser {
}
private RootBeanDefinition createFilterBean(String loginUrl, String defaultTargetUrl, String alwaysUseDefault,
String loginPage, String authenticationFailureUrl, String successHandlerRef, String failureHandlerRef) {
String loginPage, String authenticationFailureUrl, String successHandlerRef, String failureHandlerRef,
String authDetailsSourceRef) {
BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.rootBeanDefinition(filterClassName);
@ -136,6 +140,10 @@ public class FormLoginBeanDefinitionParser {
filterBuilder.addPropertyValue("authenticationSuccessHandler", successHandler.getBeanDefinition());
}
if (StringUtils.hasText(authDetailsSourceRef)) {
filterBuilder.addPropertyReference("authenticationDetailsSource", authDetailsSourceRef);
}
if (sessionStrategy != null) {
filterBuilder.addPropertyValue("sessionAuthenticationStrategy", sessionStrategy);
}

View File

@ -187,10 +187,10 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
logger.info("Checking sorted filter chain: " + filters);
for(int i=0; i < filters.size(); i++) {
OrderDecorator filter = (OrderDecorator)filters.get(i);
OrderDecorator filter = filters.get(i);
if (i > 0) {
OrderDecorator previous = (OrderDecorator)filters.get(i-1);
OrderDecorator previous = filters.get(i-1);
if (filter.getOrder() == previous.getOrder()) {
pc.getReaderContext().error("Filter beans '" + filter.bean + "' and '" +
previous.bean + "' have the same 'order' value. When using custom filters, " +

View File

@ -381,6 +381,10 @@ form-login.attlist &=
form-login.attlist &=
## Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination
attribute authentication-failure-handler-ref {xsd:token}?
form-login.attlist &=
## Reference to an AuthenticationDetailsSource which will be used by the authentication filter
attribute authentication-details-source-ref {xsd:token}?
openid-login =
## Sets up form login for authentication with an Open ID identity
@ -451,6 +455,9 @@ http-basic =
http-basic.attlist &=
## Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.
attribute entry-point-ref {xsd:token}?
http-basic.attlist &=
## Reference to an AuthenticationDetailsSource which will be used by the authentication filter
attribute authentication-details-source-ref {xsd:token}?
session-management =
element session-management {session-management.attlist, concurrency-control?}
@ -565,6 +572,8 @@ x509.attlist &=
x509.attlist &=
## Explicitly specifies which user-service should be used to load user data for X.509 authenticated clients. If ommitted, the default user-service will be used.
user-service-ref?
x509.attlist &=
attribute authentication-details-source-ref {xsd:token}?
jee =
## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.

View File

@ -926,6 +926,11 @@
<xs:documentation>Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="authentication-details-source-ref" type="xs:token">
<xs:annotation>
<xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication filter</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:element name="attribute-exchange"><xs:annotation>
@ -1059,6 +1064,11 @@
<xs:documentation>Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="authentication-details-source-ref" type="xs:token">
<xs:annotation>
<xs:documentation>Reference to an AuthenticationDetailsSource which will be used by the authentication filter</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:attributeGroup name="session-management.attlist">
@ -1167,7 +1177,7 @@
<xs:attributeGroup name="remember-me-services-ref">
<xs:attribute name="services-ref" type="xs:token">
<xs:annotation>
<xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same "key" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider.</xs:documentation>
<xs:documentation>Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same "key" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. It should also implement the LogoutHandler interface, which will be invoked when a user logs out. Typically the remember-me cookie would be removed on logout.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
@ -1217,6 +1227,7 @@
<xs:documentation>A reference to a user-service (or UserDetailsService bean) Id</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="authentication-details-source-ref" type="xs:token"/>
</xs:attributeGroup>
<xs:element name="jee"><xs:annotation>
<xs:documentation>Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.</xs:documentation>

View File

@ -1,18 +1,11 @@
package org.springframework.security.config.http
import groovy.lang.Closure;
import groovy.xml.MarkupBuilder
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.Filter
import org.springframework.mock.web.MockFilterChain
import org.springframework.mock.web.MockHttpServletRequest
import org.springframework.mock.web.MockHttpServletResponse
import org.springframework.security.config.AbstractXmlConfigTests
import org.springframework.security.config.BeanIds
import org.springframework.security.config.util.InMemoryXmlApplicationContext
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.web.FilterChainProxy
import org.springframework.security.web.FilterInvocation

View File

@ -44,7 +44,8 @@ import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
import org.springframework.security.web.session.SessionManagementFilter;
import groovy.lang.Closure;
import groovy.lang.Closure
import org.springframework.security.openid.OpenIDAuthenticationFilter;
class MiscHttpConfigTests extends AbstractHttpConfigTests {
def 'Minimal configuration parses'() {
@ -502,6 +503,23 @@ class MiscHttpConfigTests extends AbstractHttpConfigTests {
roles.contains 'ROLE_user'
roles.contains 'ROLE_c'
}
def authenticationDetailsSourceInjectionSucceeds() {
xml.http() {
'form-login'('authentication-details-source-ref' : 'adsr')
'openid-login' ('authentication-details-source-ref' : 'adsr')
'http-basic' ('authentication-details-source-ref' : 'adsr')
'x509' ('authentication-details-source-ref' : 'adsr')
}
bean('adsr', 'org.springframework.security.web.authentication.WebAuthenticationDetailsSource')
createAppContext()
def adsr = appContext.getBean('adsr')
expect:
getFilter(UsernamePasswordAuthenticationFilter).authenticationDetailsSource == adsr
getFilter(OpenIDAuthenticationFilter).authenticationDetailsSource == adsr
getFilter(BasicAuthenticationFilter).authenticationDetailsSource == adsr
getFilter(X509AuthenticationFilter).authenticationDetailsSource == adsr
}
}
class MockEntryPoint extends LoginUrlAuthenticationEntryPoint {

View File

@ -196,6 +196,10 @@ public abstract class AbstractPreAuthenticatedProcessingFilter extends GenericFi
this.authenticationDetailsSource = authenticationDetailsSource;
}
protected AuthenticationDetailsSource<HttpServletRequest, ?> getAuthenticationDetailsSource() {
return authenticationDetailsSource;
}
/**
* @param authenticationManager
* The AuthenticationManager to use