diff --git a/config/src/main/java/org/springframework/security/config/Elements.java b/config/src/main/java/org/springframework/security/config/Elements.java
index 0b48cd7c05..04b359f6a9 100644
--- a/config/src/main/java/org/springframework/security/config/Elements.java
+++ b/config/src/main/java/org/springframework/security/config/Elements.java
@@ -46,6 +46,7 @@ public abstract class Elements {
public static final String CUSTOM_FILTER = "custom-filter";
public static final String REQUEST_CACHE = "request-cache";
public static final String X509 = "x509";
+ public static final String JEE = "jee";
public static final String FILTER_SECURITY_METADATA_SOURCE = "filter-security-metadata-source";
public static final String METHOD_SECURITY_METADATA_SOURCE = "method-security-metadata-source";
@Deprecated
diff --git a/config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java b/config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java
index d42b37ac18..8576ffccff 100644
--- a/config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java
+++ b/config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java
@@ -25,16 +25,22 @@ import org.springframework.security.authentication.AnonymousAuthenticationProvid
import org.springframework.security.authentication.RememberMeAuthenticationProvider;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.Elements;
+import org.springframework.security.core.authority.mapping.SimpleAttributes2GrantedAuthoritiesMapper;
+import org.springframework.security.core.authority.mapping.SimpleMappableAttributesRetriever;
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;
+import org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesUserDetailsService;
+import org.springframework.security.web.authentication.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource;
+import org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter;
import org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor;
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
+import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
@@ -88,9 +94,13 @@ final class AuthenticationConfigBuilder {
private String formFilterId = null;
private String openIDFilterId = null;
private BeanDefinition x509Filter;
- private BeanDefinition x509EntryPoint;
private BeanReference x509ProviderRef;
private String x509ProviderId;
+ private BeanDefinition jeeFilter;
+ private BeanReference jeeProviderRef;
+ private RootBeanDefinition preAuthEntryPoint;
+ private String jeeProviderId;
+
private BeanDefinition logoutFilter;
private BeanDefinition loginPageGenerationFilter;
private BeanDefinition etf;
@@ -119,6 +129,7 @@ final class AuthenticationConfigBuilder {
createFormLoginFilter(sessionStrategy, authenticationManager);
createOpenIDLoginFilter(sessionStrategy, authenticationManager);
createX509Filter(authenticationManager);
+ createJeeFilter(authenticationManager);
createLogoutFilter();
createLoginPageFilterIfNeeded();
createUserDetailsServiceFactory();
@@ -318,7 +329,6 @@ final class AuthenticationConfigBuilder {
void createX509Filter(BeanReference authManager) {
Element x509Elt = DomUtils.getChildElementByTagName(httpElt, Elements.X509);
RootBeanDefinition filter = null;
- RootBeanDefinition entryPoint = null;
if (x509Elt != null) {
BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.rootBeanDefinition(X509AuthenticationFilter.class);
@@ -334,14 +344,12 @@ final class AuthenticationConfigBuilder {
filterBuilder.addPropertyValue("principalExtractor", extractor.getBeanDefinition());
}
filter = (RootBeanDefinition) filterBuilder.getBeanDefinition();
- entryPoint = new RootBeanDefinition(Http403ForbiddenEntryPoint.class);
- entryPoint.setSource(pc.extractSource(x509Elt));
+ createPrauthEntryPoint(x509Elt);
createX509Provider();
}
x509Filter = filter;
- x509EntryPoint = entryPoint;
}
private void createX509Provider() {
@@ -359,6 +367,67 @@ final class AuthenticationConfigBuilder {
x509ProviderRef = new RuntimeBeanReference(x509ProviderId);
}
+ private void createPrauthEntryPoint(Element source) {
+ if (preAuthEntryPoint == null) {
+ preAuthEntryPoint = new RootBeanDefinition(Http403ForbiddenEntryPoint.class);
+ preAuthEntryPoint.setSource(pc.extractSource(source));
+ }
+ }
+
+ void createJeeFilter(BeanReference authManager) {
+ final String ATT_MAPPABLE_ROLES = "mappable-roles";
+
+ Element jeeElt = DomUtils.getChildElementByTagName(httpElt, Elements.JEE);
+ RootBeanDefinition filter = null;
+
+ if (jeeElt != null) {
+ BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.rootBeanDefinition(J2eePreAuthenticatedProcessingFilter.class);
+ filterBuilder.getRawBeanDefinition().setSource(pc.extractSource(jeeElt));
+ filterBuilder.addPropertyValue("authenticationManager", authManager);
+
+ BeanDefinitionBuilder adsBldr = BeanDefinitionBuilder.rootBeanDefinition(J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource.class);
+ adsBldr.addPropertyValue("userRoles2GrantedAuthoritiesMapper", new RootBeanDefinition(SimpleAttributes2GrantedAuthoritiesMapper.class));
+
+ String roles = jeeElt.getAttribute(ATT_MAPPABLE_ROLES);
+ Assert.state(StringUtils.hasText(roles));
+ BeanDefinitionBuilder rolesBuilder = BeanDefinitionBuilder.rootBeanDefinition(StringUtils.class);
+ rolesBuilder.addConstructorArgValue(roles);
+ rolesBuilder.setFactoryMethod("commaDelimitedListToSet");
+
+ RootBeanDefinition mappableRolesRetriever = new RootBeanDefinition(SimpleMappableAttributesRetriever.class);
+ mappableRolesRetriever.getPropertyValues().addPropertyValue("mappableAttributes", rolesBuilder.getBeanDefinition());
+ adsBldr.addPropertyValue("mappableRolesRetriever", mappableRolesRetriever);
+ filterBuilder.addPropertyValue("authenticationDetailsSource", adsBldr.getBeanDefinition());
+
+ filter = (RootBeanDefinition) filterBuilder.getBeanDefinition();
+
+ createPrauthEntryPoint(jeeElt);
+ createJeeProvider();
+ }
+
+ jeeFilter = filter;
+ }
+
+ private void createJeeProvider() {
+ Element jeeElt = DomUtils.getChildElementByTagName(httpElt, Elements.JEE);
+ BeanDefinition provider = new RootBeanDefinition(PreAuthenticatedAuthenticationProvider.class);
+
+ RootBeanDefinition uds;
+ if (StringUtils.hasText(jeeElt.getAttribute(ATT_USER_SERVICE_REF))) {
+ uds = new RootBeanDefinition();
+ uds.setFactoryBeanName(BeanIds.USER_DETAILS_SERVICE_FACTORY);
+ uds.setFactoryMethodName("authenticationUserDetailsService");
+ uds.getConstructorArgumentValues().addGenericArgumentValue(jeeElt.getAttribute(ATT_USER_SERVICE_REF));
+ } else {
+ uds = new RootBeanDefinition(PreAuthenticatedGrantedAuthoritiesUserDetailsService.class);
+ }
+
+ provider.getPropertyValues().addPropertyValue("preAuthenticatedUserDetailsService", uds);
+
+ jeeProviderId = pc.getReaderContext().registerWithGeneratedName(provider);
+ jeeProviderRef = new RuntimeBeanReference(jeeProviderId);
+ }
+
void createLoginPageFilterIfNeeded() {
boolean needLoginPage = formFilter != null || openIDFilter != null;
String formLoginPage = getLoginFormUrl(formEntryPoint);
@@ -521,9 +590,9 @@ final class AuthenticationConfigBuilder {
return openIDEntryPoint;
}
- // If X.509 has been enabled, use the preauth entry point.
- if (DomUtils.getChildElementByTagName(httpElt, Elements.X509) != null) {
- return x509EntryPoint;
+ // If X.509 or JEE have been enabled, use the preauth entry point.
+ if (preAuthEntryPoint != null) {
+ return preAuthEntryPoint;
}
pc.getReaderContext().error("No AuthenticationEntryPoint could be established. Please " +
@@ -581,6 +650,10 @@ final class AuthenticationConfigBuilder {
filters.add(new OrderDecorator(x509Filter, X509_FILTER));
}
+ if (jeeFilter != null) {
+ filters.add(new OrderDecorator(jeeFilter, PRE_AUTH_FILTER));
+ }
+
if (formFilter != null) {
filters.add(new OrderDecorator(formFilter, FORM_LOGIN_FILTER));
}
@@ -621,6 +694,10 @@ final class AuthenticationConfigBuilder {
providers.add(x509ProviderRef);
}
+ if (jeeProviderRef != null) {
+ providers.add(jeeProviderRef);
+ }
+
return providers;
}
diff --git a/config/src/main/java/org/springframework/security/config/http/UserDetailsServiceFactoryBean.java b/config/src/main/java/org/springframework/security/config/http/UserDetailsServiceFactoryBean.java
index c52961e535..dc41c4f4a9 100644
--- a/config/src/main/java/org/springframework/security/config/http/UserDetailsServiceFactoryBean.java
+++ b/config/src/main/java/org/springframework/security/config/http/UserDetailsServiceFactoryBean.java
@@ -58,7 +58,7 @@ public class UserDetailsServiceFactoryBean implements ApplicationContextAware {
if (!beans.isEmpty()) {
if (beans.size() > 1) {
throw new ApplicationContextException("More than one AuthenticationUserDetailsService registered." +
- "Please use a specific Id reference in element.");
+ " Please use a specific Id reference.");
}
return (AuthenticationUserDetailsService) beans.values().toArray()[0];
}
diff --git a/config/src/main/resources/org/springframework/security/config/spring-security-3.1.rnc b/config/src/main/resources/org/springframework/security/config/spring-security-3.1.rnc
index 9c6fd542d0..d4d1e13a72 100644
--- a/config/src/main/resources/org/springframework/security/config/spring-security-3.1.rnc
+++ b/config/src/main/resources/org/springframework/security/config/spring-security-3.1.rnc
@@ -256,7 +256,7 @@ protect-pointcut.attlist &=
http =
## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the "secured" attribute to "false".
- element http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & openid-login? & x509? & http-basic? & logout? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache?) }
+ element http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & openid-login? & x509? & jee? & http-basic? & logout? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache?) }
http.attlist &=
## The request URL pattern which will be mapped to the filter chain created by this element. If omitted, the filter chain will match all requests.
attribute pattern {xsd:token}?
@@ -566,6 +566,16 @@ 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?
+jee =
+ ## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.
+ element jee {jee.attlist}
+jee.attlist &=
+ ## A comma-separate list of roles to look for in the incoming HttpServletRequest.
+ attribute mappable-roles {xsd:token}
+jee.attlist &=
+ ## Explicitly specifies which user-service should be used to load user data for container authenticated clients. If ommitted, the set of mappable-roles will be used to construct the authorities for the user.
+ user-service-ref?
+
authentication-manager =
## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.
element authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*}
diff --git a/config/src/main/resources/org/springframework/security/config/spring-security-3.1.xsd b/config/src/main/resources/org/springframework/security/config/spring-security-3.1.xsd
index 9cbaaed04f..f238e20ac5 100644
--- a/config/src/main/resources/org/springframework/security/config/spring-security-3.1.xsd
+++ b/config/src/main/resources/org/springframework/security/config/spring-security-3.1.xsd
@@ -641,6 +641,7 @@
+
Adds support for basic authentication
@@ -1212,6 +1213,23 @@
+
+ Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.
+
+
+
+
+
+
+ A comma-separate list of roles to look for in the incoming HttpServletRequest.
+
+
+
+
+ A reference to a user-service (or UserDetailsService bean) Id
+
+
+
Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.
diff --git a/config/src/test/groovy/org/springframework/security/config/http/MiscHttpConfigTests.groovy b/config/src/test/groovy/org/springframework/security/config/http/MiscHttpConfigTests.groovy
index 249323524c..c78e5da9c6 100644
--- a/config/src/test/groovy/org/springframework/security/config/http/MiscHttpConfigTests.groovy
+++ b/config/src/test/groovy/org/springframework/security/config/http/MiscHttpConfigTests.groovy
@@ -1,5 +1,6 @@
package org.springframework.security.config.http;
+import java.security.Principal
import java.util.Collection;
import java.util.Map;
import java.util.Iterator;
@@ -17,6 +18,8 @@ import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.context.support.AbstractXmlApplicationContext
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.util.InMemoryXmlApplicationContext;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.core.context.SecurityContext
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.util.FieldUtils;
import org.springframework.security.access.AccessDeniedException
@@ -471,6 +474,34 @@ class MiscHttpConfigTests extends AbstractHttpConfigTests {
expect:
getFilter(UsernamePasswordAuthenticationFilter).authenticationManager.eraseCredentialsAfterAuthentication == false
}
+
+ def jeeFilterExtractsExpectedRoles() {
+ xml.http() {
+ jee('mappable-roles': 'admin,user,a,b,c')
+ }
+ createAppContext()
+ FilterChainProxy fcp = appContext.getBean(BeanIds.FILTER_CHAIN_PROXY)
+ Principal p = Mock(Principal)
+ p.getName() >> 'joe'
+
+ when:
+
+ MockHttpServletRequest request = new MockHttpServletRequest("GET","/something")
+ request.setUserPrincipal(p)
+ request.addUserRole('admin')
+ request.addUserRole('user')
+ request.addUserRole('c')
+ request.addUserRole('notmapped')
+ fcp.doFilter(request, new MockHttpServletResponse(), new MockFilterChain())
+ SecurityContext ctx = request.getSession().getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
+ Set roles = AuthorityUtils.authorityListToSet(ctx.getAuthentication().getAuthorities());
+
+ then:
+ roles.size() == 3
+ roles.contains 'ROLE_admin'
+ roles.contains 'ROLE_user'
+ roles.contains 'ROLE_c'
+ }
}
class MockEntryPoint extends LoginUrlAuthenticationEntryPoint {
diff --git a/core/src/main/java/org/springframework/security/core/authority/mapping/SimpleMappableAttributesRetriever.java b/core/src/main/java/org/springframework/security/core/authority/mapping/SimpleMappableAttributesRetriever.java
index b49c85c011..bab867e2a5 100755
--- a/core/src/main/java/org/springframework/security/core/authority/mapping/SimpleMappableAttributesRetriever.java
+++ b/core/src/main/java/org/springframework/security/core/authority/mapping/SimpleMappableAttributesRetriever.java
@@ -24,8 +24,7 @@ public class SimpleMappableAttributesRetriever implements MappableAttributesRetr
return mappableAttributes;
}
- @SuppressWarnings("unchecked")
- public void setMappableAttributes(Set aMappableRoles) {
+ public void setMappableAttributes(Set aMappableRoles) {
mappableAttributes = new HashSet();
mappableAttributes.addAll(aMappableRoles);
mappableAttributes = Collections.unmodifiableSet(mappableAttributes);