Announcement Announcement Module
Collapse
No announcement yet.
CustomUserDetailsService can't do @Autowired Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • CustomUserDetailsService can't do @Autowired

    I'm trying to do my own CustomUserDetailsService and looking up the database via my DAO. Unfortunately, I can not do @Autowired within this Service.

    Here is my code.

    PHP Code:
    @Service
    @Transactional(readOnly true)
    public class 
    CustomUserDetailsService implements UserDetailsService {

        @
    Autowired
        
    private PersonDAO personDAO;

        public 
    UserDetails loadUserByUsername(String emailthrows UsernameNotFoundException {

            
    UserDetails user null;
            
    Person person null;
            
            try {
                
    // It fails here!!!!
                
    person personDAO.getPersonemail );
                
                
    user = new SecurityUserpersonperson.getFirstName()+" "+person.getLastName(), 
                        
    person.getPassword().toLowerCase(), getGrantedAuthoritiesperson.getRoleList() ) );
                
                return 
    user;
                
            } catch (
    Exception e) {
                
    System.out.println"Exception message: "+e.getMessage() );
                
    System.out.println"Exception cause: "+e.getCause() );
                throw new 
    RuntimeException(e);
            }
        }
        
        
        public static List<
    GrantedAuthoritygetGrantedAuthorities(List<Roleroles) {
            List<
    GrantedAuthorityauthorities = new ArrayList<GrantedAuthority>();
            for( 
    Role role roles ) {
                
    authorities.add( new SimpleGrantedAuthorityrole.getRoleName() ));
            }
            return 
    authorities;
        }

    The security.xml file:
    PHP Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans:beans xmlns="http://www.springframework.org/schema/security"
        xmlns:beans="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                            http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">

        <debug />

        <global-method-security pre-post-annotations="enabled" />

        <http use-expressions="true">
            <intercept-url pattern="/account/**" access="hasRole('ROLE_USER')" />
            
            <form-login login-page="/auth/login" 
                        authentication-failure-url="/auth/denied" 
                        default-target-url="/" />

            <access-denied-handler error-page="/auth/denied" />

            <logout invalidate-session="true"
                    logout-success-url="/auth/logout"
                    logout-url="/logout"
                    delete-cookies="JSESSIONID" />
                    
            <remember-me />
            
            <session-management invalid-session-url="/">
                <concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
            </session-management>

        </http>

        <beans:bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" />
        
        <authentication-manager>
            <authentication-provider user-service-ref="customUserDetailsService">
                <password-encoder ref="passwordEncoder"/>
            </authentication-provider>
        </authentication-manager>
        

    </beans:beans>
    The Problem is, that all my services in my Controller classes are working fine and I'm connecting to the DB, but not if I'm trying to get a user from my CustomUserDetailsService.java. This is really weird.... It seems like my PersonDAO is not initialized at all.

    If the function loadUserByUsername(email) is called I'm catching the Exception with the output: null.
    I would appreciate any hints or suggestions!

    Thanks in advance.

  • #2
    And why should it work? I see nothing in your configuration to drive annotation based configuration. I would expect a component-scan annotation or xml defined userdetailsservice with a context:annotation-config . But neither is showing.

    Comment


    • #3
      Sorry, you are right. I should point out other definitions as well.

      I have a another applicationContext.xml file where my
      PHP Code:
      <context:component-scan base-package="com.company" /> 
      is already defined.

      And here is my Java Configuration file:
      PHP Code:
      @Configuration
      @EnableWebMvc
      @EnableTransactionManagement
      @EnableScheduling
      public class AppConfig  extends WebMvcConfigurerAdapter {

          private static 
      Log log LogFactory.getLogAppConfig.class );
          
          public 
      void addResourceHandlersResourceHandlerRegistry registry ) {
              
      registry.addResourceHandler"/resources/**" ).addResourceLocations"/resources/" );
          }
          
          @
      Bean
          
      public ViewResolver viewResolver() {
              
      InternalResourceViewResolver resolver = new InternalResourceViewResolver();
              
      resolver.setViewClassJstlView.class );
              
      resolver.setPrefix"/WEB-INF/jsp/" );
              
      resolver.setSuffix".jsp" );
              return 
      resolver;
          }
          
          @
      Bean
          
      public MessageSource messageSource() {
              
      ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
              
      messageSource.setBasename("/WEB-INF/messages/messages");
              return 
      messageSource;
          }

          @
      Bean
          
      public TilesConfigurer tilesConfigurer() {
              
      TilesConfigurer configurer = new TilesConfigurer();
              
      configurer.setDefinitions(new String[] {
                  
      "/WEB-INF/jsp/layout/layouts.xml",
                  
      "/WEB-INF/jsp/*/views.xml"                
              
      });
              
      configurer.setCheckRefresh(true);
              return 
      configurer;
          }
          
          @
      Bean
          
      public UrlBasedViewResolver urlBasedViewResolver() {
              
      UrlBasedViewResolver viewResolver = new UrlBasedViewResolver();
              
      viewResolver.setViewClassTilesView.class );
              
      viewResolver.setOrder);
              return 
      viewResolver;
          }
          
          @
      Bean
          
      public ControllerClassNameHandlerMapping controllerClassNameHandlerMapping() {
              
      ControllerClassNameHandlerMapping hm = new ControllerClassNameHandlerMapping();
              
      hm.setDefaultHandler( new UrlFilenameViewController() );
              return 
      hm;
          }
          

      If I call any other controller, where I do @Autowired, it's working fine. Only within CustomUserDetailsService.java I'm having this problem. Is it maybe related to the fact that my security-context.xml is configured outsite of my AppConfig.java file?

      I know it sounds strange, but I have no explanation for it. Funny though, that the same configuration with spring-security-core-3.1.0.RC2 and spring-framework 3.1.0.M2 it was working, but not with the latest stable RELEASES of spring framework together with spring security....

      Comment


      • #4
        Assuming you have a ContextLoaderListener and a DispatcherServlet I really hope you aren't loading things twice! I would expect the AppConfig to be loaded by the dispatcher servlet. Also bean(factory)postprocessor (which is what makes the @Autowire work) work only in the application context which they are defined in.

        Some minor tips on your appconfig JstlView is by default for tiles use the TilesViewResolver this saves you a couple of lines .

        Comment


        • #5
          Thanks for the hint! I changed it to TilesViewResolver.

          Speaking of the DispatcherServlet and ContextLoaderListener. To be honest, I never understood it 100%. If you could take a look on my web.xml, that would be great.

          PHP Code:
          <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
              
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              
          xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
              
              <
          display-name>gwitty</display-name>
              
              
              <
          listener>
                  <
          listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
              </
          listener>
              
              <
          listener>
                  <
          listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
              </
          listener>
              
              <
          context-param>
                  <
          param-name>webAppRootKey</param-name>
                  <
          param-value>company.root</param-value>
              </
          context-param>
            
              <
          context-param>
                  <
          param-name>log4jConfigLocation</param-name>
                  <
          param-value>classpath:log4j.xml</param-value>
              </
          context-param>
              
              <
          context-param>
                  <
          param-name>log4jExposeWebAppRoot</param-name>
                  <
          param-value>false</param-value>
              </
          context-param>
              
              <
          context-param>
                  <
          param-name>contextConfigLocation</param-name>
                  <
          param-value>/WEB-INF/spring/*.xml</param-value>
              </context-param>
              
              
              <!-- Reads request input using UTF-8 encoding -->
              <filter>
                  <filter-name>characterEncodingFilter</filter-name>
                  <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
                  <init-param>
                      <param-name>encoding</param-name>
                      <param-value>UTF-8</param-value>
                  </init-param>
                  <init-param>
                      <param-name>forceEncoding</param-name>
                      <param-value>true</param-value>
                  </init-param>
              </filter>
              
              
              <filter>
                  <filter-name>UrlRewriteFilter</filter-name>
                  <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
              </filter>
           

              <filter>
                  <filter-name>hibernateFilter</filter-name>
                  <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
                  <init-param>
                      <param-name>singleSession</param-name>
                      <param-value>true</param-value>
                  </init-param>
              </filter>
              
              
              <filter>
                  <filter-name>springSecurityFilterChain</filter-name>
                  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
              </filter>

              <filter-mapping>
                <filter-name>springSecurityFilterChain</filter-name>
                <url-pattern>/*</url-pattern>
              </filter-mapping>
              
              
              <filter-mapping>
                  <filter-name>hibernateFilter</filter-name>
                  <url-pattern>/*</url-pattern>
              </filter-mapping>

              <filter-mapping>
                  <filter-name>characterEncodingFilter</filter-name>
                  <url-pattern>/*</url-pattern>
              </filter-mapping>

              <filter-mapping>
                  <filter-name>UrlRewriteFilter</filter-name>
                  <url-pattern>/*</url-pattern>
              </filter-mapping>    
              
              
              
              
              <servlet>
                  <servlet-name>spring</servlet-name>
                  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
                  <init-param>
                      <param-name>contextConfigLocation</param-name>
                      <param-value></param-value>
                  </init-param>
                  <load-on-startup>1</load-on-startup>
              </servlet>
              
              <!-- H2 Database Console for managing the app's database -->
              <servlet>
                  <servlet-name>H2Console</servlet-name>
                  <servlet-class>org.h2.server.web.WebServlet</servlet-class>
                  <init-param>
                      <param-name>-webAllowOthers</param-name>
                      <param-value>true</param-value>
                  </init-param>
                  <load-on-startup>2</load-on-startup>
              </servlet>


              <servlet-mapping>
                  <servlet-name>spring</servlet-name>
                  <url-pattern>/home/*</url-pattern>
              </servlet-mapping>
              
              <servlet-mapping>
                  <servlet-name>spring</servlet-name>
                  <url-pattern>/</url-pattern>
              </servlet-mapping>
              
              <error-page>
                  <error-code>404</error-code>
                  <location>/404</location>
              </error-page>

              
              <!-- Default page to serve -->
              <welcome-file-list>
                  <welcome-file>/welcome</welcome-file>
              </welcome-file-list>
           
          </web-app> 

          Comment


          • #6
            Well judging by your configuration all is loaded by the ContextLoaderListener, your DispatcherServlet doesn't load anything (empty contextConfigLocation initialization parameter).

            However that should also make your configuration work. Can you zip all configuration related files (java, xml and web.xml) that might help clarifying things.

            Comment


            • #7
              Hi Marten,

              Thanks for your reply. Please, find attached all related configuration files together with the domain Object. As I said, The only problem is the CustomeUserDetailsService, which fails here. All other annotated as @Service classes are working fine.

              I really appreciate your help.

              Attachment
              Attached Files

              Comment


              • #8
                Hi!

                I actually found out another post, where somebody has exactly the same problem and maybe I described my problem not in detail enough.

                I realized that you (Marten) replied to it.

                http://forum.springsource.org/showth...DetailsService

                So, I tried to do the following security-application-context:

                HTML Code:
                <?xml version="1.0" encoding="UTF-8"?>
                <beans:beans xmlns="http://www.springframework.org/schema/security"
                    xmlns:beans="http://www.springframework.org/schema/beans"
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                    xmlns:context="http://www.springframework.org/schema/context"
                    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                        				http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
                                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
                
                    <context:component-scan base-package="com.company" />
                    
                    <debug />
                
                    <global-method-security pre-post-annotations="enabled" />
                
                    <http use-expressions="true">
                    	<intercept-url pattern="/account/**" access="hasRole('ROLE_USER')" />
                		
                		<form-login login-page="/auth/login" 
                					authentication-failure-url="/auth/denied" 
                					default-target-url="/contact/feedback" />
                
                		<access-denied-handler error-page="/auth/denied" />
                
                		<logout invalidate-session="true"
                				logout-success-url="/auth/logout"
                				logout-url="/logout"
                				delete-cookies="JSESSIONID" />
                				
                        <remember-me />
                        
                        <!-- Uncomment to limit the number of sessions a user can have -->
                        <session-management invalid-session-url="/">
                            <concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
                        </session-management>
                
                    </http>
                
                	<beans:bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" />
                	<beans:bean id="personDAO" class="com.company.dao.impl.PersonDaoImpl" />
                	<beans:bean id="customUserDetailsService" class="com.company.auth.CustomUserDetailsService">
                		<beans:property name="personImpl" ref="personDAO"/>
                	</beans:bean>
                	
                	<authentication-manager>
                		<authentication-provider user-service-ref="customUserDetailsService">
                			<password-encoder ref="passwordEncoder"/>
                		</authentication-provider>
                	</authentication-manager>
                	
                </beans:beans>
                Unfortunately, I'm still getting the same error. I'm not sure if your answer is what I did...?

                Comment


                • #9
                  The problem is that you now have 2 instances of your bean (and maybe more depending on your java config). The xml configuration is going to be overridden by the beans picked up by the component-scan, now if you also construct a new CustomUserDetailService in your java config that is also going to add to the problems.

                  If you have it in xml then don't have a @Service as that is also picked up by component-scan.

                  Comment


                  • #10
                    Alright, it's (somehow) working now. I'm getting some test outputs coming from my PersonDaoImpl, but now if I want to look up for the user in the database, of course I'm using the sessionFactory bean, which is Injected. I guess, I have to setup my sessionFactory bean also inside the security-context.xml file as well, right?

                    Comment


                    • #11
                      Here is my PersonDaoImpl:

                      PHP Code:
                      @Repository
                      public class PersonDaoImpl implements PersonDAO {
                          
                          private 
                      Log logger LogFactory.getLoggetClass() );

                          @
                      Autowired
                          
                      private SessionFactory sessionFactory;

                              
                      // it's working
                          
                      public String test() {
                              
                      System.out.println"inside impl repo" );
                              return 
                      "testing";
                          }
                          
                              
                      // No problem calling the method
                          
                      public Person getPersonString email ) {
                              
                      Person person null;
                              try {
                                  
                      // fails here...
                                  
                      Session session sessionFactory.getCurrentSession();
                                  
                      Criteria crit session.createCriteriaPerson.class );
                                  
                                  
                      person = ( Person )crit.addRestrictions.eq"email"email ) ).uniqueResult();
                              } catch( 
                      Exception e ) {
                                  
                      System.out.println"error: "+e.getMessage() );
                                  
                      System.out.println"error: "+e.getCause() );
                              }
                              return 
                      person;
                          }

                      Comment


                      • #12
                        All your services/daos etc. should be loaded in the same context as the security (in general the ContextLoaderListener) it doesn't matter in which XML file they are as long as they are loaded in the SAME context.

                        Beware make sure you aren't scanning for beans twice or loading the same xml in different contexts (ContextLoaderListener and DispatcherServlet) as that can lead to all sorts of issues.

                        Comment


                        • #13
                          But this means, that now I have to setup my sessionFactory inside the xml configuration and not inside my Java config (DataConfig.java), right? I was hoping that the xml configuration is being loaded in the same context...

                          Comment


                          • #14
                            No you don't... a context can be expressed in different (and even combined) ways xml, properties files and java. All these different files are merged together in an ApplicationContext. However as mentioned before make sure that you only load them once and not multiple times.

                            Comment


                            • #15
                              OK, got it, but why is the Injection inside my PersonDaoImpl not working? It fails here:

                              PHP Code:
                              Session session sessionFactory.getCurrentSession(); 
                              I can't figure out, what I'm doing wrong. I took out the annotation inside the CustomUserDetailsService and I can execute a normal test function, but of course not the one where I'm trying to look up in the database. Here is my latest code:

                              application-context.xml
                              PHP Code:
                              <?xml version="1.0" encoding="UTF-8"?>
                              <beans:beans xmlns="http://www.springframework.org/schema/security"
                                  xmlns:beans="http://www.springframework.org/schema/beans"
                                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                  xmlns:context="http://www.springframework.org/schema/context"
                                  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                                                      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
                                                      http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">

                                  <!-- context:component-scan base-package="com.company" / -->
                                  
                                  <debug />

                                  <global-method-security pre-post-annotations="enabled" />

                                  <http use-expressions="true">
                                      <intercept-url pattern="/account/**" access="hasRole('ROLE_USER')" />
                                      
                                      <form-login login-page="/auth/login" 
                                                  authentication-failure-url="/auth/denied" 
                                                  default-target-url="/contact/feedback" />

                                      <access-denied-handler error-page="/auth/denied" />

                                      <logout invalidate-session="true"
                                              logout-success-url="/auth/logout"
                                              logout-url="/logout"
                                              delete-cookies="JSESSIONID" />
                                              
                                      <remember-me />
                                      
                                      <session-management invalid-session-url="/">
                                          <concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
                                      </session-management>

                                  </http>

                                  
                                  <beans:bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" />
                                  <beans:bean id="personDAO" class="com.company.dao.impl.PersonDaoImpl" />    
                                  <beans:bean id="customUserDetailsService" class="com.company.auth.CustomUserDetailsService">
                                      <beans:property name="personImpl" ref="personDAO"/>
                                  </beans:bean>
                                  
                                  <authentication-manager>
                                      <authentication-provider user-service-ref="customUserDetailsService">
                                          <password-encoder ref="passwordEncoder"/>
                                      </authentication-provider>
                                  </authentication-manager>
                                  
                              </beans:beans>
                              and here is a snippet of the CustomUserDetailsService.java:
                              PHP Code:
                              @Transactional(readOnly true)
                              public class 
                              CustomUserDetailsService implements UserDetailsService {

                                  private 
                              PersonDAO personDAO;

                                  public 
                              void setPersonImplPersonDAO personDAO ) {
                                      
                              this.personDAO personDAO;
                                  }
                                  
                                  public 
                              UserDetails loadUserByUsername(String email) {

                                      
                              UserDetails user null;
                                      
                              Person person null;
                                      
                                      try {
                                          
                                          
                              person personDAO.getPersonemail );            
                                          
                              user = new SecurityUserpersonperson.getFirstName()+" "+person.getLastName(), 
                                                  
                              person.getPassword().toLowerCase(), getGrantedAuthoritiesperson.getRoleList() ) );
                                          
                                          return 
                              user;
                                          
                                      } catch (
                              Exception e) {
                                          
                              System.out.println"Exception: "+e.getMessage() );
                                          
                              System.out.println"Exception: "+e.getCause() );
                                          throw new 
                              RuntimeException(e);
                                      }
                                  }
                                  
                                  
                                  
                              /**
                                   * Wraps {@link String} roles to {@link SimpleGrantedAuthority} objects
                                   * @param roles {@link String} of roles
                                   * @return list of granted authorities
                                   */
                                  
                              public static List<GrantedAuthoritygetGrantedAuthorities(List<Roleroles) {
                                      List<
                              GrantedAuthorityauthorities = new ArrayList<GrantedAuthority>();
                                      for( 
                              Role role roles ) {
                                          
                              authorities.add( new SimpleGrantedAuthorityrole.getRoleName() ));
                                      }
                                      return 
                              authorities;
                                  }

                              Where my PersonDaoImpl.java fails while injecting the SessionFactory:
                              PHP Code:
                              public class PersonDaoImpl implements PersonDAO {
                                  
                                  private 
                              Log logger LogFactory.getLoggetClass() );

                                  @
                              Autowired
                                  
                              private SessionFactory sessionFactory;


                                  public 
                              String test() {
                                      
                              System.out.println"inside impl repo" );
                                      return 
                              "testing";
                                  }
                                  
                                  public 
                              Person getPersonString email ) {
                                      
                              Person person null;
                                      try {
                                          
                              System.out.println"step 1" );
                                          
                              Session session sessionFactory.getCurrentSession();
                                          
                              System.out.println"step 2" );
                                          
                              Criteria crit session.createCriteriaPerson.class );
                                          
                              System.out.println"step 3" );
                                          
                                          
                              person = ( Person )crit.addRestrictions.eq"email"email ) ).uniqueResult();
                                          
                              System.out.println"step 4" );
                                          
                              System.out.println"person: "+person.getDomain() );            
                                      } catch( 
                              Exception e ) {
                                          
                              System.out.println"error: "+e.getMessage() );
                                          
                              System.out.println"error: "+e.getCause() );
                                      }
                                      return 
                              person;
                                  }

                              This is the output, I'm getting:
                              HTML Code:
                              step 1
                              error: null
                              error: null
                              Exception: null
                              Exception: null

                              Comment

                              Working...
                              X