View Javadoc

1   package org.springframework.security.config;
2   
3   import org.springframework.beans.BeansException;
4   import org.springframework.beans.PropertyValue;
5   import org.springframework.beans.factory.config.BeanDefinition;
6   import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
7   import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
8   import org.springframework.beans.factory.config.RuntimeBeanReference;
9   import org.springframework.beans.factory.parsing.BeanComponentDefinition;
10  import org.springframework.beans.factory.support.BeanDefinitionBuilder;
11  import org.springframework.beans.factory.support.RootBeanDefinition;
12  import org.springframework.beans.factory.xml.BeanDefinitionParser;
13  import org.springframework.beans.factory.xml.ParserContext;
14  import org.springframework.core.Ordered;
15  import org.springframework.security.providers.dao.DaoAuthenticationProvider;
16  import org.springframework.util.StringUtils;
17  import org.springframework.util.xml.DomUtils;
18  import org.w3c.dom.Element;
19  
20  /**
21   * Wraps a UserDetailsService bean with a DaoAuthenticationProvider and registers the latter with the
22   * ProviderManager.
23   *
24   * @author Luke Taylor
25   * @version $Id: AuthenticationProviderBeanDefinitionParser.java 3194 2008-07-30 11:01:23Z luke_t $
26   */
27  class AuthenticationProviderBeanDefinitionParser implements BeanDefinitionParser {
28      private static String ATT_USER_DETAILS_REF = "user-service-ref";
29  
30      public BeanDefinition parse(Element element, ParserContext parserContext) {
31          RootBeanDefinition authProvider = new RootBeanDefinition(DaoAuthenticationProvider.class);
32          authProvider.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
33          authProvider.setSource(parserContext.extractSource(element));
34  
35          Element passwordEncoderElt = DomUtils.getChildElementByTagName(element, Elements.PASSWORD_ENCODER);
36  
37          if (passwordEncoderElt != null) {
38              PasswordEncoderParser pep = new PasswordEncoderParser(passwordEncoderElt, parserContext);
39              authProvider.getPropertyValues().addPropertyValue("passwordEncoder", pep.getPasswordEncoder());
40  
41              if (pep.getSaltSource() != null) {
42                  authProvider.getPropertyValues().addPropertyValue("saltSource", pep.getSaltSource());
43              }
44          }
45  
46          Element userServiceElt = DomUtils.getChildElementByTagName(element, Elements.USER_SERVICE);
47          Element jdbcUserServiceElt = DomUtils.getChildElementByTagName(element, Elements.JDBC_USER_SERVICE);
48          Element ldapUserServiceElt = DomUtils.getChildElementByTagName(element, Elements.LDAP_USER_SERVICE);
49  
50          // We need to register the provider to access it in the post processor to check if it has a cache
51          final String id = parserContext.getReaderContext().generateBeanName(authProvider);
52          parserContext.getRegistry().registerBeanDefinition(id, authProvider);                    
53          parserContext.registerComponent(new BeanComponentDefinition(authProvider, id));
54          
55          String ref = element.getAttribute(ATT_USER_DETAILS_REF);        
56          
57          if (StringUtils.hasText(ref)) {            
58              if (userServiceElt != null || jdbcUserServiceElt != null || ldapUserServiceElt != null) {
59                  parserContext.getReaderContext().error("The " + ATT_USER_DETAILS_REF + " attribute cannot be used in combination with child" +
60                          "elements '" + Elements.USER_SERVICE + "', '" + Elements.JDBC_USER_SERVICE + "' or '" +
61                          Elements.LDAP_USER_SERVICE + "'", element);
62              }
63          } else {
64              // Use the child elements to create the UserDetailsService
65              AbstractUserDetailsServiceBeanDefinitionParser parser = null;
66              Element elt = null;
67      
68              if (userServiceElt != null) {
69                  elt = userServiceElt;
70                  parser = new UserServiceBeanDefinitionParser();
71              } else if (jdbcUserServiceElt != null) {
72                  elt = jdbcUserServiceElt;
73                  parser = new JdbcUserServiceBeanDefinitionParser();
74              } else if (ldapUserServiceElt != null) {
75                  elt = ldapUserServiceElt;
76                  parser = new LdapUserServiceBeanDefinitionParser();
77              } else {
78                  parserContext.getReaderContext().error("A user-service is required", element);
79              }
80              
81              parser.parse(elt, parserContext);
82              ref = parser.getId();
83          }
84          
85          authProvider.getPropertyValues().addPropertyValue("userDetailsService", new RuntimeBeanReference(ref));        
86  
87          BeanDefinitionBuilder cacheResolverBldr = BeanDefinitionBuilder.rootBeanDefinition(AuthenticationProviderCacheResolver.class);        
88          cacheResolverBldr.addConstructorArg(id);
89          cacheResolverBldr.addConstructorArg(ref);        
90          cacheResolverBldr.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
91          BeanDefinition cacheResolver = cacheResolverBldr.getBeanDefinition();
92          
93          String name = parserContext.getReaderContext().generateBeanName(cacheResolver);
94          parserContext.getRegistry().registerBeanDefinition(name , cacheResolver);
95          parserContext.registerComponent(new BeanComponentDefinition(cacheResolver, name));
96          
97          ConfigUtils.addAuthenticationProvider(parserContext, id);        
98          
99          return null;
100     }
101     
102     /**
103      * Checks whether the registered user service bean has an associated cache and, if so, sets it on the 
104      * authentication provider.
105      */
106     static class AuthenticationProviderCacheResolver implements BeanFactoryPostProcessor, Ordered {
107         private String providerId;
108         private String userServiceId;
109         
110         public AuthenticationProviderCacheResolver(String providerId, String userServiceId) {
111             this.providerId = providerId;
112             this.userServiceId = userServiceId;
113         }
114 
115         public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
116             RootBeanDefinition provider = (RootBeanDefinition) beanFactory.getBeanDefinition(providerId);
117             
118             String cachingId = userServiceId + AbstractUserDetailsServiceBeanDefinitionParser.CACHING_SUFFIX;
119             
120             if (beanFactory.containsBeanDefinition(cachingId)) {
121                 RootBeanDefinition cachingUserService = (RootBeanDefinition) beanFactory.getBeanDefinition(cachingId);
122             
123                 PropertyValue userCacheProperty = cachingUserService.getPropertyValues().getPropertyValue("userCache");
124                 
125                 provider.getPropertyValues().addPropertyValue(userCacheProperty);
126             }
127         }
128 
129         public int getOrder() {
130             return HIGHEST_PRECEDENCE;
131         }
132     }
133 }