View Javadoc

1   package org.springframework.security.config;
2   
3   import java.util.Collections;
4   import java.util.Iterator;
5   import java.util.LinkedHashMap;
6   import java.util.List;
7   import java.util.Map;
8   
9   import org.apache.commons.logging.Log;
10  import org.apache.commons.logging.LogFactory;
11  import org.springframework.beans.factory.config.BeanDefinition;
12  import org.springframework.beans.factory.config.RuntimeBeanReference;
13  import org.springframework.beans.factory.support.BeanDefinitionBuilder;
14  import org.springframework.beans.factory.support.BeanDefinitionRegistry;
15  import org.springframework.beans.factory.support.RootBeanDefinition;
16  import org.springframework.beans.factory.support.ManagedList;
17  import org.springframework.beans.factory.xml.BeanDefinitionParser;
18  import org.springframework.beans.factory.xml.ParserContext;
19  import org.springframework.security.ConfigAttributeDefinition;
20  import org.springframework.security.ConfigAttributeEditor;
21  import org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter;
22  import org.springframework.security.context.HttpSessionContextIntegrationFilter;
23  import org.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource;
24  import org.springframework.security.intercept.web.FilterSecurityInterceptor;
25  import org.springframework.security.intercept.web.RequestKey;
26  import org.springframework.security.securechannel.ChannelDecisionManagerImpl;
27  import org.springframework.security.securechannel.ChannelProcessingFilter;
28  import org.springframework.security.securechannel.InsecureChannelProcessor;
29  import org.springframework.security.securechannel.SecureChannelProcessor;
30  import org.springframework.security.securechannel.RetryWithHttpEntryPoint;
31  import org.springframework.security.securechannel.RetryWithHttpsEntryPoint;
32  import org.springframework.security.ui.AccessDeniedHandlerImpl;
33  import org.springframework.security.ui.ExceptionTranslationFilter;
34  import org.springframework.security.ui.SessionFixationProtectionFilter;
35  import org.springframework.security.ui.webapp.DefaultLoginPageGeneratingFilter;
36  import org.springframework.security.util.FilterChainProxy;
37  import org.springframework.security.util.RegexUrlPathMatcher;
38  import org.springframework.security.util.AntUrlPathMatcher;
39  import org.springframework.security.util.UrlMatcher;
40  import org.springframework.util.StringUtils;
41  import org.springframework.util.xml.DomUtils;
42  import org.w3c.dom.Element;
43  
44  /**
45   * Sets up HTTP security: filter stack and protected URLs.
46   *
47   * @author Luke Taylor
48   * @author Ben Alex
49   * @since 2.0
50   * @version $Id: HttpSecurityBeanDefinitionParser.java 3246 2008-08-11 21:15:09Z luke_t $
51   */
52  public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
53      static final Log logger = LogFactory.getLog(HttpSecurityBeanDefinitionParser.class);
54  
55      static final String ATT_REALM = "realm";
56      static final String DEF_REALM = "Spring Security Application";
57  
58      static final String ATT_PATH_PATTERN = "pattern";
59  
60      static final String ATT_SESSION_FIXATION_PROTECTION = "session-fixation-protection";
61      static final String OPT_SESSION_FIXATION_NO_PROTECTION = "none";
62      static final String OPT_SESSION_FIXATION_CLEAN_SESSION = "newSession";
63      static final String OPT_SESSION_FIXATION_MIGRATE_SESSION = "migrateSession";
64  
65      static final String ATT_PATH_TYPE = "path-type";
66      static final String DEF_PATH_TYPE_ANT = "ant";
67      static final String OPT_PATH_TYPE_REGEX = "regex";
68  
69      static final String ATT_FILTERS = "filters";
70      static final String OPT_FILTERS_NONE = "none";
71  
72      static final String ATT_ACCESS_CONFIG = "access";
73      static final String ATT_REQUIRES_CHANNEL = "requires-channel";
74      static final String OPT_REQUIRES_HTTP = "http";
75      static final String OPT_REQUIRES_HTTPS = "https";
76      static final String OPT_ANY_CHANNEL = "any";
77  
78      static final String ATT_HTTP_METHOD = "method";
79  
80      static final String ATT_CREATE_SESSION = "create-session";
81      static final String DEF_CREATE_SESSION_IF_REQUIRED = "ifRequired";
82      static final String OPT_CREATE_SESSION_ALWAYS = "always";
83      static final String OPT_CREATE_SESSION_NEVER = "never";
84  
85      static final String ATT_LOWERCASE_COMPARISONS = "lowercase-comparisons";
86      static final String DEF_LOWERCASE_COMPARISONS = "true";
87  
88      static final String ATT_AUTO_CONFIG = "auto-config";
89      static final String DEF_AUTO_CONFIG = "false";
90  
91      static final String ATT_SERVLET_API_PROVISION = "servlet-api-provision";
92      static final String DEF_SERVLET_API_PROVISION = "true";
93  
94      static final String ATT_ACCESS_MGR = "access-decision-manager-ref";
95      static final String ATT_USER_SERVICE_REF = "user-service-ref";
96  
97      static final String ATT_ENTRY_POINT_REF = "entry-point-ref";
98      static final String ATT_ONCE_PER_REQUEST = "once-per-request";
99      static final String ATT_ACCESS_DENIED_PAGE = "access-denied-page";
100 
101     public BeanDefinition parse(Element element, ParserContext parserContext) {
102         ConfigUtils.registerProviderManagerIfNecessary(parserContext);
103         final BeanDefinitionRegistry registry = parserContext.getRegistry();
104         final UrlMatcher matcher = createUrlMatcher(element);
105         final Object source = parserContext.extractSource(element);
106         // SEC-501 - should paths stored in request maps be converted to lower case
107         // true if Ant path and using lower case
108         final boolean convertPathsToLowerCase = (matcher instanceof AntUrlPathMatcher) && matcher.requiresLowerCaseUrl();
109 
110         final List interceptUrlElts = DomUtils.getChildElementsByTagName(element, Elements.INTERCEPT_URL);
111         final Map filterChainMap =  new LinkedHashMap();
112         final LinkedHashMap channelRequestMap = new LinkedHashMap();
113 
114         registerFilterChainProxy(parserContext, filterChainMap, matcher, source);
115 
116         parseInterceptUrlsForChannelSecurityAndFilterChain(interceptUrlElts, filterChainMap, channelRequestMap,
117                 convertPathsToLowerCase, parserContext);
118 
119         boolean allowSessionCreation = registerHttpSessionIntegrationFilter(element, parserContext);
120 
121         registerServletApiFilter(element, parserContext);
122 
123         // Set up the access manager reference for http
124         String accessManagerId = element.getAttribute(ATT_ACCESS_MGR);
125 
126         if (!StringUtils.hasText(accessManagerId)) {
127             ConfigUtils.registerDefaultAccessManagerIfNecessary(parserContext);
128             accessManagerId = BeanIds.ACCESS_MANAGER;
129         }
130 
131         // Register the portMapper. A default will always be created, even if no element exists.
132         BeanDefinition portMapper = new PortMappingsBeanDefinitionParser().parse(
133                 DomUtils.getChildElementByTagName(element, Elements.PORT_MAPPINGS), parserContext);
134         registry.registerBeanDefinition(BeanIds.PORT_MAPPER, portMapper);
135 
136         registerExceptionTranslationFilter(element, parserContext, allowSessionCreation);
137 
138 
139         if (channelRequestMap.size() > 0) {
140             // At least one channel requirement has been specified
141             registerChannelProcessingBeans(parserContext, matcher, channelRequestMap);
142         }
143 
144         registerFilterSecurityInterceptor(element, parserContext, matcher, accessManagerId,
145                 parseInterceptUrlsForFilterInvocationRequestMap(interceptUrlElts, convertPathsToLowerCase, parserContext));
146 
147         boolean sessionControlEnabled = registerConcurrentSessionControlBeansIfRequired(element, parserContext);
148 
149         registerSessionFixationProtectionFilter(parserContext, element.getAttribute(ATT_SESSION_FIXATION_PROTECTION),
150                 sessionControlEnabled);
151 
152         boolean autoConfig = false;
153         if ("true".equals(element.getAttribute(ATT_AUTO_CONFIG))) {
154             autoConfig = true;
155         }
156 
157         Element anonymousElt = DomUtils.getChildElementByTagName(element, Elements.ANONYMOUS);
158         if (anonymousElt != null || autoConfig) {
159             new AnonymousBeanDefinitionParser().parse(anonymousElt, parserContext);
160         }
161 
162         parseRememberMeAndLogout(element, autoConfig, parserContext);
163 
164         parseBasicFormLoginAndOpenID(element, parserContext, autoConfig, allowSessionCreation);
165 
166         Element x509Elt = DomUtils.getChildElementByTagName(element, Elements.X509);
167         if (x509Elt != null) {
168             new X509BeanDefinitionParser().parse(x509Elt, parserContext);
169         }
170 
171         // Register the post processors which will tie up the loose ends in the configuration once the app context has been created and all beans are available.
172         RootBeanDefinition postProcessor = new RootBeanDefinition(EntryPointInjectionBeanPostProcessor.class);
173         postProcessor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
174         registry.registerBeanDefinition(BeanIds.ENTRY_POINT_INJECTION_POST_PROCESSOR, postProcessor);
175         RootBeanDefinition postProcessor2 = new RootBeanDefinition(UserDetailsServiceInjectionBeanPostProcessor.class);
176         postProcessor2.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
177         registry.registerBeanDefinition(BeanIds.USER_DETAILS_SERVICE_INJECTION_POST_PROCESSOR, postProcessor2);
178 
179         return null;
180     }
181 
182     private void parseRememberMeAndLogout(Element elt, boolean autoConfig, ParserContext pc) {
183         // Parse remember me before logout as RememberMeServices is also a LogoutHandler implementation.
184         Element rememberMeElt = DomUtils.getChildElementByTagName(elt, Elements.REMEMBER_ME);
185         String rememberMeServices = null;
186 
187         if (rememberMeElt != null || autoConfig) {
188             RememberMeBeanDefinitionParser rmbdp = new RememberMeBeanDefinitionParser();
189             rmbdp.parse(rememberMeElt, pc);
190             rememberMeServices = rmbdp.getServicesName();
191             // Post processor to inject RememberMeServices into filters which need it
192             RootBeanDefinition rememberMeInjectionPostProcessor = new RootBeanDefinition(RememberMeServicesInjectionBeanPostProcessor.class);
193             rememberMeInjectionPostProcessor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
194             pc.getRegistry().registerBeanDefinition(BeanIds.REMEMBER_ME_SERVICES_INJECTION_POST_PROCESSOR, rememberMeInjectionPostProcessor);
195         }
196 
197         Element logoutElt = DomUtils.getChildElementByTagName(elt, Elements.LOGOUT);
198         if (logoutElt != null || autoConfig) {
199             new LogoutBeanDefinitionParser(rememberMeServices).parse(logoutElt, pc);
200         }
201     }
202 
203     private void registerFilterChainProxy(ParserContext pc, Map filterChainMap, UrlMatcher matcher, Object source) {
204         if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY)) {
205             pc.getReaderContext().error("Duplicate <http> element detected", source);
206         }
207 
208         RootBeanDefinition filterChainProxy = new RootBeanDefinition(FilterChainProxy.class);
209         filterChainProxy.setSource(source);
210         filterChainProxy.getPropertyValues().addPropertyValue("matcher", matcher);
211         filterChainProxy.getPropertyValues().addPropertyValue("stripQueryStringFromUrls", Boolean.valueOf(matcher instanceof AntUrlPathMatcher));
212         filterChainProxy.getPropertyValues().addPropertyValue("filterChainMap", filterChainMap);
213         pc.getRegistry().registerBeanDefinition(BeanIds.FILTER_CHAIN_PROXY, filterChainProxy);
214         pc.getRegistry().registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN);
215     }
216 
217     private boolean registerHttpSessionIntegrationFilter(Element element, ParserContext pc) {
218         RootBeanDefinition httpScif = new RootBeanDefinition(HttpSessionContextIntegrationFilter.class);
219         boolean sessionCreationAllowed = true;
220 
221         String createSession = element.getAttribute(ATT_CREATE_SESSION);
222         if (OPT_CREATE_SESSION_ALWAYS.equals(createSession)) {
223             httpScif.getPropertyValues().addPropertyValue("allowSessionCreation", Boolean.TRUE);
224             httpScif.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
225         } else if (OPT_CREATE_SESSION_NEVER.equals(createSession)) {
226             httpScif.getPropertyValues().addPropertyValue("allowSessionCreation", Boolean.FALSE);
227             httpScif.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
228             sessionCreationAllowed = false;
229         } else {
230             createSession = DEF_CREATE_SESSION_IF_REQUIRED;
231             httpScif.getPropertyValues().addPropertyValue("allowSessionCreation", Boolean.TRUE);
232             httpScif.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
233         }
234 
235         pc.getRegistry().registerBeanDefinition(BeanIds.HTTP_SESSION_CONTEXT_INTEGRATION_FILTER, httpScif);
236         ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.HTTP_SESSION_CONTEXT_INTEGRATION_FILTER));
237 
238         return sessionCreationAllowed;
239     }
240 
241     // Adds the servlet-api integration filter if required
242     private void registerServletApiFilter(Element element, ParserContext pc) {
243         String provideServletApi = element.getAttribute(ATT_SERVLET_API_PROVISION);
244         if (!StringUtils.hasText(provideServletApi)) {
245             provideServletApi = DEF_SERVLET_API_PROVISION;
246         }
247 
248         if ("true".equals(provideServletApi)) {
249             pc.getRegistry().registerBeanDefinition(BeanIds.SECURITY_CONTEXT_HOLDER_AWARE_REQUEST_FILTER,
250                     new RootBeanDefinition(SecurityContextHolderAwareRequestFilter.class));
251             ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.SECURITY_CONTEXT_HOLDER_AWARE_REQUEST_FILTER));
252         }
253     }
254 
255     private boolean registerConcurrentSessionControlBeansIfRequired(Element element, ParserContext parserContext) {
256         Element sessionControlElt = DomUtils.getChildElementByTagName(element, Elements.CONCURRENT_SESSIONS);
257         if (sessionControlElt == null) {
258             return false;
259         }
260 
261         new ConcurrentSessionsBeanDefinitionParser().parse(sessionControlElt, parserContext);
262         logger.info("Concurrent session filter in use, setting 'forceEagerSessionCreation' to true");
263         BeanDefinition sessionIntegrationFilter = parserContext.getRegistry().getBeanDefinition(BeanIds.HTTP_SESSION_CONTEXT_INTEGRATION_FILTER);
264         sessionIntegrationFilter.getPropertyValues().addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
265         return true;
266     }
267 
268     private void registerExceptionTranslationFilter(Element element, ParserContext pc, boolean allowSessionCreation) {
269         String accessDeniedPage = element.getAttribute(ATT_ACCESS_DENIED_PAGE);
270         ConfigUtils.validateHttpRedirect(accessDeniedPage, pc, pc.extractSource(element));
271         BeanDefinitionBuilder exceptionTranslationFilterBuilder
272             = BeanDefinitionBuilder.rootBeanDefinition(ExceptionTranslationFilter.class);
273         exceptionTranslationFilterBuilder.addPropertyValue("createSessionAllowed", new Boolean(allowSessionCreation));
274 
275         if (StringUtils.hasText(accessDeniedPage)) {
276             AccessDeniedHandlerImpl accessDeniedHandler = new AccessDeniedHandlerImpl();
277             accessDeniedHandler.setErrorPage(accessDeniedPage);
278             exceptionTranslationFilterBuilder.addPropertyValue("accessDeniedHandler", accessDeniedHandler);
279         }
280 
281         pc.getRegistry().registerBeanDefinition(BeanIds.EXCEPTION_TRANSLATION_FILTER, exceptionTranslationFilterBuilder.getBeanDefinition());
282         ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.EXCEPTION_TRANSLATION_FILTER));
283     }
284 
285     private void registerFilterSecurityInterceptor(Element element, ParserContext pc, UrlMatcher matcher,
286             String accessManagerId, LinkedHashMap filterInvocationDefinitionMap) {
287         BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(FilterSecurityInterceptor.class);
288 
289         builder.addPropertyReference("accessDecisionManager", accessManagerId);
290         builder.addPropertyReference("authenticationManager", BeanIds.AUTHENTICATION_MANAGER);
291 
292         if ("false".equals(element.getAttribute(ATT_ONCE_PER_REQUEST))) {
293             builder.addPropertyValue("observeOncePerRequest", Boolean.FALSE);
294         }
295 
296         DefaultFilterInvocationDefinitionSource fids =
297             new DefaultFilterInvocationDefinitionSource(matcher, filterInvocationDefinitionMap);
298         fids.setStripQueryStringFromUrls(matcher instanceof AntUrlPathMatcher);
299 
300         builder.addPropertyValue("objectDefinitionSource", fids);
301         pc.getRegistry().registerBeanDefinition(BeanIds.FILTER_SECURITY_INTERCEPTOR, builder.getBeanDefinition());
302         ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.FILTER_SECURITY_INTERCEPTOR));
303     }
304 
305     private void registerChannelProcessingBeans(ParserContext pc, UrlMatcher matcher, LinkedHashMap channelRequestMap) {
306         RootBeanDefinition channelFilter = new RootBeanDefinition(ChannelProcessingFilter.class);
307         channelFilter.getPropertyValues().addPropertyValue("channelDecisionManager",
308                 new RuntimeBeanReference(BeanIds.CHANNEL_DECISION_MANAGER));
309         DefaultFilterInvocationDefinitionSource channelFilterInvDefSource =
310             new DefaultFilterInvocationDefinitionSource(matcher, channelRequestMap);
311         channelFilterInvDefSource.setStripQueryStringFromUrls(matcher instanceof AntUrlPathMatcher);
312 
313         channelFilter.getPropertyValues().addPropertyValue("filterInvocationDefinitionSource",
314                 channelFilterInvDefSource);
315         RootBeanDefinition channelDecisionManager = new RootBeanDefinition(ChannelDecisionManagerImpl.class);
316         ManagedList channelProcessors = new ManagedList(3);
317         RootBeanDefinition secureChannelProcessor = new RootBeanDefinition(SecureChannelProcessor.class);
318         RootBeanDefinition retryWithHttp = new RootBeanDefinition(RetryWithHttpEntryPoint.class);
319         RootBeanDefinition retryWithHttps = new RootBeanDefinition(RetryWithHttpsEntryPoint.class);
320         RuntimeBeanReference portMapper = new RuntimeBeanReference(BeanIds.PORT_MAPPER);
321         retryWithHttp.getPropertyValues().addPropertyValue("portMapper", portMapper);
322         retryWithHttps.getPropertyValues().addPropertyValue("portMapper", portMapper);
323         secureChannelProcessor.getPropertyValues().addPropertyValue("entryPoint", retryWithHttps);
324         RootBeanDefinition inSecureChannelProcessor = new RootBeanDefinition(InsecureChannelProcessor.class);
325         inSecureChannelProcessor.getPropertyValues().addPropertyValue("entryPoint", retryWithHttp);
326         channelProcessors.add(secureChannelProcessor);
327         channelProcessors.add(inSecureChannelProcessor);
328         channelDecisionManager.getPropertyValues().addPropertyValue("channelProcessors", channelProcessors);
329 
330         pc.getRegistry().registerBeanDefinition(BeanIds.CHANNEL_PROCESSING_FILTER, channelFilter);
331         ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.CHANNEL_PROCESSING_FILTER));
332         pc.getRegistry().registerBeanDefinition(BeanIds.CHANNEL_DECISION_MANAGER, channelDecisionManager);
333 
334     }
335 
336     private void registerSessionFixationProtectionFilter(ParserContext pc, String sessionFixationAttribute, boolean sessionControlEnabled) {
337         if(!StringUtils.hasText(sessionFixationAttribute)) {
338             sessionFixationAttribute = OPT_SESSION_FIXATION_MIGRATE_SESSION;
339         }
340 
341         if (!sessionFixationAttribute.equals(OPT_SESSION_FIXATION_NO_PROTECTION)) {
342             BeanDefinitionBuilder sessionFixationFilter =
343                 BeanDefinitionBuilder.rootBeanDefinition(SessionFixationProtectionFilter.class);
344             sessionFixationFilter.addPropertyValue("migrateSessionAttributes",
345                     Boolean.valueOf(sessionFixationAttribute.equals(OPT_SESSION_FIXATION_MIGRATE_SESSION)));
346             if (sessionControlEnabled) {
347                 sessionFixationFilter.addPropertyReference("sessionRegistry", BeanIds.SESSION_REGISTRY);
348             }
349             pc.getRegistry().registerBeanDefinition(BeanIds.SESSION_FIXATION_PROTECTION_FILTER,
350                     sessionFixationFilter.getBeanDefinition());
351             ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.SESSION_FIXATION_PROTECTION_FILTER));
352         }
353     }
354 
355     private void parseBasicFormLoginAndOpenID(Element element, ParserContext pc, boolean autoConfig, boolean allowSessionCreation) {
356         RootBeanDefinition formLoginFilter = null;
357         RootBeanDefinition formLoginEntryPoint = null;
358         String formLoginPage = null;
359         RootBeanDefinition openIDFilter = null;
360         RootBeanDefinition openIDEntryPoint = null;
361         String openIDLoginPage = null;
362 
363         String realm = element.getAttribute(ATT_REALM);
364         if (!StringUtils.hasText(realm)) {
365             realm = DEF_REALM;
366         }
367 
368         Element basicAuthElt = DomUtils.getChildElementByTagName(element, Elements.BASIC_AUTH);
369         if (basicAuthElt != null || autoConfig) {
370             new BasicAuthenticationBeanDefinitionParser(realm).parse(basicAuthElt, pc);
371         }
372 
373         Element formLoginElt = DomUtils.getChildElementByTagName(element, Elements.FORM_LOGIN);
374 
375         if (formLoginElt != null || autoConfig) {
376             FormLoginBeanDefinitionParser parser = new FormLoginBeanDefinitionParser("/j_spring_security_check",
377                     "org.springframework.security.ui.webapp.AuthenticationProcessingFilter");
378 
379             parser.parse(formLoginElt, pc);
380             formLoginFilter = parser.getFilterBean();
381             formLoginEntryPoint = parser.getEntryPointBean();
382             formLoginPage = parser.getLoginPage();
383         }
384 
385         Element openIDLoginElt = DomUtils.getChildElementByTagName(element, Elements.OPENID_LOGIN);
386 
387         if (openIDLoginElt != null) {
388             FormLoginBeanDefinitionParser parser = new FormLoginBeanDefinitionParser("/j_spring_openid_security_check",
389                     "org.springframework.security.ui.openid.OpenIDAuthenticationProcessingFilter");
390 
391             parser.parse(openIDLoginElt, pc);
392             openIDFilter = parser.getFilterBean();
393             openIDEntryPoint = parser.getEntryPointBean();
394             openIDLoginPage = parser.getLoginPage();
395 
396             BeanDefinitionBuilder openIDProviderBuilder =
397                 BeanDefinitionBuilder.rootBeanDefinition("org.springframework.security.providers.openid.OpenIDAuthenticationProvider");
398 
399             String userService = openIDLoginElt.getAttribute(ATT_USER_SERVICE_REF);
400 
401             if (StringUtils.hasText(userService)) {
402                 openIDProviderBuilder.addPropertyReference("userDetailsService", userService);
403             }
404 
405             BeanDefinition openIDProvider = openIDProviderBuilder.getBeanDefinition();
406             pc.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_PROVIDER, openIDProvider);
407             ConfigUtils.addAuthenticationProvider(pc, BeanIds.OPEN_ID_PROVIDER);
408         }
409 
410         boolean needLoginPage = false;
411 
412         if (formLoginFilter != null) {
413             needLoginPage = true;
414             formLoginFilter.getPropertyValues().addPropertyValue("allowSessionCreation", new Boolean(allowSessionCreation));
415             pc.getRegistry().registerBeanDefinition(BeanIds.FORM_LOGIN_FILTER, formLoginFilter);
416             ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.FORM_LOGIN_FILTER));
417             pc.getRegistry().registerBeanDefinition(BeanIds.FORM_LOGIN_ENTRY_POINT, formLoginEntryPoint);
418         }
419 
420         if (openIDFilter != null) {
421             needLoginPage = true;
422             openIDFilter.getPropertyValues().addPropertyValue("allowSessionCreation", new Boolean(allowSessionCreation));
423             pc.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_FILTER, openIDFilter);
424             ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.OPEN_ID_FILTER));
425             pc.getRegistry().registerBeanDefinition(BeanIds.OPEN_ID_ENTRY_POINT, openIDEntryPoint);
426         }
427 
428         // If no login page has been defined, add in the default page generator.
429         if (needLoginPage && formLoginPage == null && openIDLoginPage == null) {
430             logger.info("No login page configured. The default internal one will be used. Use the '"
431                      + FormLoginBeanDefinitionParser.ATT_LOGIN_PAGE + "' attribute to set the URL of the login page.");
432             BeanDefinitionBuilder loginPageFilter =
433                 BeanDefinitionBuilder.rootBeanDefinition(DefaultLoginPageGeneratingFilter.class);
434 
435             if (formLoginFilter != null) {
436                 loginPageFilter.addConstructorArg(new RuntimeBeanReference(BeanIds.FORM_LOGIN_FILTER));
437             }
438 
439             if (openIDFilter != null) {
440                 loginPageFilter.addConstructorArg(new RuntimeBeanReference(BeanIds.OPEN_ID_FILTER));
441             }
442 
443             pc.getRegistry().registerBeanDefinition(BeanIds.DEFAULT_LOGIN_PAGE_GENERATING_FILTER,
444                     loginPageFilter.getBeanDefinition());
445             ConfigUtils.addHttpFilter(pc, new RuntimeBeanReference(BeanIds.DEFAULT_LOGIN_PAGE_GENERATING_FILTER));
446         }
447 
448         // We need to establish the main entry point.
449         // First check if a custom entry point bean is set
450         String customEntryPoint = element.getAttribute(ATT_ENTRY_POINT_REF);
451 
452         if (StringUtils.hasText(customEntryPoint)) {
453             pc.getRegistry().registerAlias(customEntryPoint, BeanIds.MAIN_ENTRY_POINT);
454             return;
455         }
456 
457         // Basic takes precedence if explicit element is used and no others are configured
458         if (basicAuthElt != null && formLoginElt == null && openIDLoginElt == null) {
459             pc.getRegistry().registerAlias(BeanIds.BASIC_AUTHENTICATION_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT);
460             return;
461         }
462 
463         // If formLogin has been enabled either through an element or auto-config, then it is used if no openID login page
464         // has been set
465         if (formLoginFilter != null && openIDLoginPage == null) {
466             pc.getRegistry().registerAlias(BeanIds.FORM_LOGIN_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT);
467             return;
468         }
469 
470         // Otherwise use OpenID if enabled
471         if (openIDFilter != null && formLoginFilter == null) {
472             pc.getRegistry().registerAlias(BeanIds.OPEN_ID_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT);
473             return;
474         }
475 
476         // If X.509 has been enabled, use the preauth entry point.
477         if (DomUtils.getChildElementByTagName(element, Elements.X509) != null) {
478             pc.getRegistry().registerAlias(BeanIds.PRE_AUTH_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT);
479             return;
480         }
481 
482         pc.getReaderContext().error("No AuthenticationEntryPoint could be established. Please " +
483                 "make sure you have a login mechanism configured through the namespace (such as form-login) or " +
484                 "specify a custom AuthenticationEntryPoint with the custom-entry-point-ref attribute ",
485                 pc.extractSource(element));
486     }
487 
488     static UrlMatcher createUrlMatcher(Element element) {
489         String patternType = element.getAttribute(ATT_PATH_TYPE);
490         if (!StringUtils.hasText(patternType)) {
491             patternType = DEF_PATH_TYPE_ANT;
492         }
493 
494         boolean useRegex = patternType.equals(OPT_PATH_TYPE_REGEX);
495 
496         UrlMatcher matcher = new AntUrlPathMatcher();
497 
498         if (useRegex) {
499             matcher = new RegexUrlPathMatcher();
500         }
501 
502         // Deal with lowercase conversion requests
503         String lowercaseComparisons = element.getAttribute(ATT_LOWERCASE_COMPARISONS);
504         if (!StringUtils.hasText(lowercaseComparisons)) {
505             lowercaseComparisons = null;
506         }
507 
508 
509         // Only change from the defaults if the attribute has been set
510         if ("true".equals(lowercaseComparisons)) {
511             if (useRegex) {
512                 ((RegexUrlPathMatcher)matcher).setRequiresLowerCaseUrl(true);
513             }
514             // Default for ant is already to force lower case
515         } else if ("false".equals(lowercaseComparisons)) {
516             if (!useRegex) {
517                 ((AntUrlPathMatcher)matcher).setRequiresLowerCaseUrl(false);
518             }
519             // Default for regex is no change
520         }
521 
522         return matcher;
523     }
524 
525     /**
526      * Parses the intercept-url elements and populates the FilterChainProxy's filter chain Map and the
527      * map used to create the FilterInvocationDefintionSource for the FilterSecurityInterceptor.
528      */
529     void parseInterceptUrlsForChannelSecurityAndFilterChain(List urlElts, Map filterChainMap,  Map channelRequestMap,
530             boolean useLowerCasePaths, ParserContext parserContext) {
531 
532         Iterator urlEltsIterator = urlElts.iterator();
533         ConfigAttributeEditor editor = new ConfigAttributeEditor();
534 
535         while (urlEltsIterator.hasNext()) {
536             Element urlElt = (Element) urlEltsIterator.next();
537 
538             String path = urlElt.getAttribute(ATT_PATH_PATTERN);
539 
540             if(!StringUtils.hasText(path)) {
541                 parserContext.getReaderContext().error("path attribute cannot be empty or null", urlElt);
542             }
543 
544             if (useLowerCasePaths) {
545                 path = path.toLowerCase();
546             }
547 
548             String requiredChannel = urlElt.getAttribute(ATT_REQUIRES_CHANNEL);
549 
550             if (StringUtils.hasText(requiredChannel)) {
551                 String channelConfigAttribute = null;
552 
553                 if (requiredChannel.equals(OPT_REQUIRES_HTTPS)) {
554                     channelConfigAttribute = "REQUIRES_SECURE_CHANNEL";
555                 } else if (requiredChannel.equals(OPT_REQUIRES_HTTP)) {
556                     channelConfigAttribute = "REQUIRES_INSECURE_CHANNEL";
557                 } else if (requiredChannel.equals(OPT_ANY_CHANNEL)) {
558                     channelConfigAttribute = ChannelDecisionManagerImpl.ANY_CHANNEL;
559                 } else {
560                     parserContext.getReaderContext().error("Unsupported channel " + requiredChannel, urlElt);
561                 }
562 
563                 editor.setAsText(channelConfigAttribute);
564                 channelRequestMap.put(new RequestKey(path), (ConfigAttributeDefinition) editor.getValue());
565             }
566 
567             String filters = urlElt.getAttribute(ATT_FILTERS);
568 
569             if (StringUtils.hasText(filters)) {
570                 if (!filters.equals(OPT_FILTERS_NONE)) {
571                     parserContext.getReaderContext().error("Currently only 'none' is supported as the custom " +
572                             "filters attribute", urlElt);
573                 }
574 
575                 filterChainMap.put(path, Collections.EMPTY_LIST);
576             }
577         }
578     }
579 
580     static LinkedHashMap parseInterceptUrlsForFilterInvocationRequestMap(List urlElts,  boolean useLowerCasePaths, ParserContext parserContext) {
581         LinkedHashMap filterInvocationDefinitionMap = new LinkedHashMap();
582 
583         Iterator urlEltsIterator = urlElts.iterator();
584         ConfigAttributeEditor editor = new ConfigAttributeEditor();
585 
586         while (urlEltsIterator.hasNext()) {
587             Element urlElt = (Element) urlEltsIterator.next();
588 
589             String path = urlElt.getAttribute(ATT_PATH_PATTERN);
590 
591             if(!StringUtils.hasText(path)) {
592                 parserContext.getReaderContext().error("path attribute cannot be empty or null", urlElt);
593             }
594 
595             if (useLowerCasePaths) {
596                 path = path.toLowerCase();
597             }
598 
599             String method = urlElt.getAttribute(ATT_HTTP_METHOD);
600             if (!StringUtils.hasText(method)) {
601                 method = null;
602             }
603 
604             String access = urlElt.getAttribute(ATT_ACCESS_CONFIG);
605 
606             // Convert the comma-separated list of access attributes to a ConfigAttributeDefinition
607             if (StringUtils.hasText(access)) {
608                 editor.setAsText(access);
609                 Object key = new RequestKey(path, method);
610 
611                 if (filterInvocationDefinitionMap.containsKey(key)) {
612                     logger.warn("Duplicate URL defined: " + key + ". The original attribute values will be overwritten");
613                 }
614 
615                 filterInvocationDefinitionMap.put(key, editor.getValue());
616             }
617         }
618 
619         return filterInvocationDefinitionMap;
620     }
621 
622 }