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
46
47
48
49
50
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
107
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
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
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
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
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
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
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
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
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
449
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
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
464
465 if (formLoginFilter != null && openIDLoginPage == null) {
466 pc.getRegistry().registerAlias(BeanIds.FORM_LOGIN_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT);
467 return;
468 }
469
470
471 if (openIDFilter != null && formLoginFilter == null) {
472 pc.getRegistry().registerAlias(BeanIds.OPEN_ID_ENTRY_POINT, BeanIds.MAIN_ENTRY_POINT);
473 return;
474 }
475
476
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
503 String lowercaseComparisons = element.getAttribute(ATT_LOWERCASE_COMPARISONS);
504 if (!StringUtils.hasText(lowercaseComparisons)) {
505 lowercaseComparisons = null;
506 }
507
508
509
510 if ("true".equals(lowercaseComparisons)) {
511 if (useRegex) {
512 ((RegexUrlPathMatcher)matcher).setRequiresLowerCaseUrl(true);
513 }
514
515 } else if ("false".equals(lowercaseComparisons)) {
516 if (!useRegex) {
517 ((AntUrlPathMatcher)matcher).setRequiresLowerCaseUrl(false);
518 }
519
520 }
521
522 return matcher;
523 }
524
525
526
527
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
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 }