9

I'm new to Spring so I been mucking around with security side of things. Everytime I run my application I get:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'securityConfig': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.springframework.security.core.userdetails.UserDetailsService com.entirety.app.config.SecurityConfig.userDetailsServiceImplementation; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.core.userdetails.UserDetailsService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

I've gone through my code with a fine comb and can't pinpoint the issue.

Spring Framework Version: 3.2.5.RELEASE Spring Security Version: 3.2.0.M2

SecurityConfig.java

package com.entirety.app.config;

import com.entirety.app.service.implement.UserDetailsServiceImplementation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

import javax.sql.DataSource;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private DataSource dataSource;

    @Autowired
    private UserDetailsService userDetailsServiceImplementation;

    @Override
    protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
        auth.jdbcAuthentication().dataSource(dataSource);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.userDetailsService(userDetailsServiceImplementation)
                .authorizeUrls()
                    .antMatchers("/admin/**").hasRole("ADMIN")
                    .antMatchers("/sec/**").hasRole("MODERATOR")
                    .antMatchers("/*").permitAll()
                    .anyRequest().anonymous().and().exceptionHandling().accessDeniedPage("/denied").and()
                .formLogin()
                    .loginProcessingUrl("/j_spring_security_check")
                    .loginPage("/login")
                    .failureUrl("/error-login")
                .and()
                .logout()
                    .logoutUrl("/j_spring_security_logout")
                    .logoutSuccessUrl("/");
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

CrmUserService.java

@Service("userService")
@Transactional
public class CrmUserService implements UserDetailsService {
    @Autowired
    private UserDAO userDAO;

    @Override
    public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException {

        com.entirety.app.domain.User domainUser = userDAO.getUser(login);

        boolean enabled = true;
        boolean accountNonExpired = true;
        boolean credentialsNonExpired = true;
        boolean accountNonLocked = true;

        return new User(
                domainUser.getLogin(),
                domainUser.getPassword(),
                enabled,
                accountNonExpired,
                credentialsNonExpired,
                accountNonLocked,
                getAuthorities(domainUser.getAuthority().getId())
        );
    }

    public Collection<? extends GrantedAuthority> getAuthorities(Integer role) {
        List<GrantedAuthority> authList = getGrantedAuthorities(getRoles(role));
        return authList;
    }

    public List<String> getRoles(Integer role) {

        List<String> roles = new ArrayList<String>();

        if (role.intValue() == 1) {
            roles.add("ROLE_MODERATOR");
            roles.add("ROLE_ADMIN");
        } else if (role.intValue() == 2) {
            roles.add("ROLE_MODERATOR");
        }
        return roles;
    }

    public static List<GrantedAuthority> getGrantedAuthorities(List<String> roles) {
        List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();

        for (String role : roles) {
            authorities.add(new SimpleGrantedAuthority(role));
        }
        return authorities;
    }
}

There is no logical reason why it should be failing yet it does.

NOTE:

My IDE can link the autowire (that is it knows where its being autowired from and everything) yet compiling it fails.

EDIT 2: I've removed the following code from SecurityConfig.java file

@Autowired
private UserDataService userDataService;

And added it to one of the POJOs and Controllers I know are called on regular basis and contain other services. In this case pasted that code into my baseController.java file which directs to rendering the homepage. The service is compile properly and i can even call it within the controller.

So the problem is isolated only to the config file such as SecurityConfig.java, thats the only time it doesn't work.

EDIT 3: Added extra files

SecurityInitializer.java

@Order(2)
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer  {
}

WebInitializer.java

@Order(1)
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] { PersistanceConfig.class, SecurityConfig.class };  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { WebConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] {"/"};
    }

//    @Override
//    protected Filter[] getServletFilters() {
//        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
//        characterEncodingFilter.setEncoding("UTF-8");
//        return new Filter[] { characterEncodingFilter};
//    }
}

mvc-dispatcher-servlet.xml

<beans xmlns="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.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.entirety.app"/>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

web.xml

<web-app version="2.4"
    xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <display-name>Spring MVC Application</display-name>

    <servlet>
        <servlet-name>mvc-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>mvc-dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
Aeseir
  • 7,754
  • 10
  • 58
  • 107
  • 3
    Is UserDetailsServiceImplementation scanned? – Dirk Lachowski Feb 03 '14 at 11:35
  • Where’s your `UserDetailsServiceImplementation` **bean** defined? – Jakub Jirutka Feb 03 '14 at 11:35
  • I suspect you forgot to component-scan the appropriate package(s). – chrylis -cautiouslyoptimistic- Feb 03 '14 at 11:36
  • I have 6 other services in same folder, all of them are being scanned and used without a problem. So its safe to assume that first thing i checked is if a) the folder was targeted by component scan b) no issues within userDetailsServiceIMplementation – Aeseir Feb 03 '14 at 11:52
  • Is the `UserDetailsService` class, in `CrmUserService` and `SecurityConfig` from the same package (`UserDetailsService` is such a common name, that you maybe imported them from two different pacakges.) – Ralph Feb 03 '14 at 12:02
  • I've modified it to CrmUserService simply to test if it is due to code conflict (as reflected by edit) still no luck. – Aeseir Feb 03 '14 at 12:04
  • Also UserDetailsService is Spring Security generic service for managing users. – Aeseir Feb 03 '14 at 12:06
  • Did you define this in beans.xml as well? – Keerthivasan Feb 03 '14 at 12:20
  • All of application is annotated so beans.xml doesn't really come into play. – Aeseir Feb 03 '14 at 12:26
  • You have a `@Service` but I see no where a `@ComponentScan` in your `@Configuration` class. Without component scanning your component never gets initialized. – M. Deinum Feb 03 '14 at 12:34
  • @ComponentScan is undertaken in my mvc-dispatcher-servlet.xml as – Aeseir Feb 03 '14 at 12:39
  • As mentioned i got 6 other services in same folder with same (or similar) configurations all work except this one. Hence the lack of logical explanation. – Aeseir Feb 03 '14 at 12:40

4 Answers4

15

Update (after providing complete example)

It seems you have multiple issues.

web.xml vs AbstractAnnotationConfigDispatcherServletInitializer

The application you created had a web.xml that configures a DispatcherServlet named mvc-dispatcher. The mvc-dispatcher had a configuration of mvc-dispatcher-servlet.xml which loaded all beans within the package com.springapp.sectest. This means that mvc-dispatcher can find your UserDetailsService.

The application also had a AbstractAnnotationConfigDispatcherServletInitializer which created a DispatcherServlet that loaded your WebConfig java configuration. This DispatcherServlet could not see any Beans created by mvc-dispatcher.

In short, you should either configure a DispatcherServlet using web.xml or AbstractAnnotationConfigDispatcherServletInitializer and NOT both.

getRootConfigClasses vs getServletConfigClasses

Any configuration within getRootConfigClasses is typically called the root or parent configuration and cannot view beans defined in getServletConfigClasses. Spring Security protects your application defined in the getRootConfigClasses with a Filter named springSecurityFilterChain that is created by the @EnableWebSecurity annotation. This means it is generally best to place Spring Security's configuration in the getRootConfigClasses. There are some exceptions to this, like if you want to do method security on your Spring MVC Controllers.

Any configuration within getServletConfigClasses is typically called the child configuration and can view beans defined in the getRootConfigClasses. The getServletConfigClasses configures the DispatcherServlet and must contain beans for Spring MVC (i.e. Controllers, ViewResovlers, etc). Any Spring MVC beans defined in getRootConfigClasses are visible but not used.

This setup, while a bit confusing, allows you to have multiple DispatcherServlet instances with isolated configurations. In practice, it is VERY rare to have multiple DispatcherServlet instances though.

Given the information above, you should move the UserDetailsService declaration along with all of its dependent beans to the getRootConfigClasses. This will ensure the SecurityConfig.java can find the UserDetailsService.

Pull Request for fixes

I have submitted a PR to get your application so that it starts with no errors https://github.com/worldcombined/AnnotateFailed/pull/1 . A few notes

  • the test does not use a parent and child context so it is not reflecting running the application
  • I had to move the resources folder so that was picked up properly
  • I did not make it so you can actually authenticate. There was no login.jsp and the UserDetailsService you provided always returns null Instead I attempted to demonstrate that the error on here was answered.

Original Answer

Based upon this comment:

@ComponentScan is undertaken in my mvc-dispatcher-servlet.xml as <context:component-scan base-package="com.entirety.app"/>

It appears that you are configuring the services within the Dispatcher configuration and not within the root context.

Spring Security's configuration is typically configured in the root context. For example, one might extend the AbstractSecurityWebApplicationInitializer as shown below:

import org.springframework.security.web.context.*;

public class SecurityWebApplicationInitializer
      extends AbstractSecurityWebApplicationInitializer {
}

How are you importing the Security configuration? If the Spring Security configuration is in the root context then it will not see beans defined in the Dispatcher Servlet context.

The solution is to move the configuring of your services in the root context or move the Spring Security configuration to the dispatcher's ApplicationContext. For example, moving Spring Security configuration to the dispatcher context would look like the following if your dispatcher servlet was named mvc.

public class SecurityWebApplicationInitializer
      extends AbstractSecurityWebApplicationInitializer {
    protected String getDispatcherWebApplicationContextSuffix() {
        return "mvc";
    }
}
Rob Winch
  • 21,440
  • 2
  • 59
  • 76
  • Yep got that all sorted. As mentioned in one of the comments i use annotation rather than XMLs for this application. So everything is setup as per annotation method. For e.g. to structure i have (or close to) you can see the YummyNoddleBar demo Spring has. I have the same structure. Except i am using customer user service. – Aeseir Feb 03 '14 at 23:05
  • I guess I missed the comment where you stated everything was now working. If you did get it sorted out, you should probably have an answer marked as accepted. – Rob Winch Feb 04 '14 at 02:49
  • Sorry Rob, that was in relation to your part of solution. I implemented it like that already and it works fine if i use basic authentication such as inMemoryAuthentication or basic jdbcAuthentication. But when I start going bit fancy and have a custom UserDetails then it falls apart. I've tried recreating the whole project only focusing on UserDetails and still same issue. Hence why its driving me insane :) – Aeseir Feb 04 '14 at 02:58
  • Please include the way in which you configured the DispatcherServlet and the root application context. If you are using web.xml this would be your web.xml. If you are using Java Configuration, it would be your WebApplicationInitializer classes. Also please include the class that is being used to load (perform the classpath scanning) the UserDetailsService. – Rob Winch Feb 04 '14 at 13:49
  • Hey Rob, i've included all the major config files for this including web.xml and dispatcher servlet.xml – Aeseir Feb 05 '14 at 12:29
  • Another thing as mentioned autowiring of this or any other service fails under SecurityConfig.java while working under all others. – Aeseir Feb 05 '14 at 13:36
  • It looks like you are registering the DispatcherServlet twice. Once in the web.xml and once in WebAppInitializer. Is there a reason you are doing this? Try using @Autowired in the PersistanceConfig. If it fails this is almost guaranteed to be an issue related to the UserDetails not getting loaded in the correct context. The root context cannot see items loaded in the child, but the child can see the parent. – Rob Winch Feb 06 '14 at 21:16
  • Hey Rob, sorry for delay, business dragged me away. I cannot work out where i got the dual registration of DispatcherServlet. Can you point it out exactly? From my understanding the WebAppInitializer extends the abstract to setup the basic context. The Web.xml points it to the actual servlet xml. This is the same way all other Spring projects and examples are setup as well. Btw im using Spring Sec 3.2.0.M2 since others don't behave and the docs are out of date. – Aeseir Feb 10 '14 at 11:02
  • If you do have any place where i can download the whole project with a customer user service that uses annotation instead of XML for configuration would be great. I could backwards engineer it to get my piece working. – Aeseir Feb 10 '14 at 11:04
  • Try moving the ComponentScan to your SecurityConfigClass as was suggested by @indybee You can find a complete sample on github at https://github.com/rwinch/gs-spring-security-3.2 There is also a webinar replay of the talk that goes with this here https://spring.io/blog/2014/01/21/webinar-replay-spring-security-3-2 – Rob Winch Feb 10 '14 at 14:13
  • Thanks Rob. I have tried indy's idea which doesn't work unfortunately. Will try and pull some of your material and test it out. I have a gut feeling annotation based security might have a bug, i'll create a brand new skeleton application and attach the github link, you can give it a go and let me know where the problem may be. Its just funky. – Aeseir Feb 12 '14 at 12:27
  • Yes please do create a sample project. I plan to release 3.2.1 on Monday so the sooner you can get me the sample the more likely any necessary fixes will be in that release. – Rob Winch Feb 12 '14 at 13:46
  • Hey Rob, please find the skeleton code for the [Annotation Sec test](https://github.com/worldcombined/AnnotateFailed) – Aeseir Feb 13 '14 at 12:10
  • Hey Rob thanks for that. After modifying the code based on your input i have located the root cause of the whole issue. Unless you specifically scan for that UserDetailsService implementation (in my case UserAccessDetails and the Autowired repositories within UserAccessDetails.java) you end up with that injection problem. Should you really be specific class scanning just for that Config? (hope that makes sense) – Aeseir Feb 15 '14 at 01:52
0

use @ component for SecurityConfig class

Abhinav Jayaram
  • 156
  • 1
  • 4
0

it should be autowired by type but...

you named your UserDetailService

@Service("userService")

try to name your field the same

@Autowired
private UserDetailsService userService;
hi_my_name_is
  • 4,894
  • 3
  • 34
  • 50
  • Tried and tested, doesn't work with that. Someone else also recommended it before, then they edited their comment. Makes me think it could be issue with the core UserDetailsService from Spring Security – Aeseir Feb 03 '14 at 12:51
  • then add @PostConstruct method to your bean, check if its created at all. You should also get spring logs while starting applications - it shows beans that were created. You can also autowire ApplicationContext ctx; and take a look at ctx.getBeanDefinitionNames() – hi_my_name_is Feb 03 '14 at 12:59
  • Thanks, checked it, cannot compile it as it throws that error, but my IDE picks up the bean tho. – Aeseir Feb 03 '14 at 22:10
0

What if try adding this annotation to SecurityConfig class?

@ComponentScan( basePackages = "package containing UserDetailsService" )

indybee
  • 1,507
  • 13
  • 17
  • Scanning works fine. As mentioned i got 6 other services in same directory and all work fine. The error seems to stemp from the Spring Security UserDetails and not my personal implementation of code. – Aeseir Feb 03 '14 at 22:09
  • adding this in case it can be of help, I had gone thru this tutorial: http://www.packtpub.com/article/spring-security-configuring-secure-passwords In the example, the authenticationManager has the following: [code] – indybee Feb 03 '14 at 22:19
  • I think this is the correct solution. The scanning is being done in the child context and should be done in the parent. Remove the scanning from the mvc config and add this annotation. – Rob Winch Feb 06 '14 at 21:17