Prashant Thorat
Prashant Thorat

Reputation: 1812

Spring security error while creating bean expected single matching bean but found 2

I am trying to implementing spring security with My Rest easy web services in spring application.I tried some basic authentications and it works perfectly.Next step I tried to create custom filters My security-context.xml is

<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:security="http://www.springframework.org/schema/security"
    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">



    <!-- SPRING SECURITY SETUP -->

    <beans:bean id="userDao" class="com.cheasyy.cofinding.dao.UserDAO">
    </beans:bean>

    <beans:bean id="passwordEncoder"
        class="org.springframework.security.crypto.password.StandardPasswordEncoder">
        <beans:constructor-arg value="ThisIsASecretSoChangeMe" />
    </beans:bean>

    <security:authentication-manager id="authenticationManager">
        <security:authentication-provider
            user-service-ref="userDao">
            <security:password-encoder ref="passwordEncoder"></security:password-encoder>
        </security:authentication-provider>
    </security:authentication-manager>

    <security:http realm="Protected API" use-expressions="true"
        auto-config="false" create-session="stateless" entry-point-ref="unauthorizedEntryPoint"
        authentication-manager-ref="authenticationManager">
        <security:custom-filter ref="authenticationTokenProcessingFilter"
            position="FORM_LOGIN_FILTER" />

        <security:intercept-url pattern="/loginService/authenticate"
            access="permitAll" />

        <security:intercept-url method="GET"
            pattern="/profileService/**" access="hasRole('user')" />

        <security:intercept-url method="PUT"
            pattern="/profileService/**" access="hasRole('admin')" />
        <security:intercept-url method="POST"
            pattern="/profileService/**" access="hasRole('admin')" />
        <security:intercept-url method="DELETE"
            pattern="/profileService/**" access="hasRole('admin')" />

    </security:http>

    <beans:bean id="unauthorizedEntryPoint"
        class="com.cheasyy.cofinding.util.UnauthorizedEntryPoint" />

    <beans:bean
        class="com.cheasyy.cofinding.util.AuthenticationTokenProcessingFilter"
        id="authenticationTokenProcessingFilter">
        <beans:constructor-arg ref="userDao" />
    </beans:bean>


</beans:beans>                          

My web.xml is

    <?xml version="1.0" encoding="UTF-8"?>
<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">

    <!-- Context Param -->
    <context-param>
        <param-name>resteasy.servlet.mapping.prefix</param-name>
        <param-value>/</param-value>
    </context-param>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring/appServlet/servlet-context.xml
            /WEB-INF/spring/appServlet/security-context.xml
        </param-value>
    </context-param>

<!-- Enables Spring Security -->

<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>/profileService/*</url-pattern>

</filter-mapping>
<listener>
        <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
    </listener>

    <!-- This SpringCotextLoader absolutely has to come after the reasteasy 
        configuration -->
    <listener>
        <listener-class>org.jboss.resteasy.plugins.spring.SpringContextLoaderListener</listener-class>
    </listener>

    <!-- Servlets -->
    <servlet>
        <servlet-name>Resteasy</servlet-name>
        <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
    </servlet>

    <servlet>
        <servlet-name>SpringMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value> /WEB-INF/spring/appServlet/servlet-context.xml </param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- Error pages -->
    <error-page>
        <error-code>400</error-code>
        <location>/400</location>
    </error-page>

    <error-page>
        <error-code>404</error-code>
        <location>/404</location>
    </error-page>

    <error-page>
        <error-code>500</error-code>
        <location>/500</location>
    </error-page>

    <!-- ALL NEW SERVICE PATHS MUST BE SPECIFIED HERE. WHENEVER A NEW SERVICE 
        IS INTRODUCED INTO THE API IT MUST BE ADDED INTO THE RESTEASY SERVLET-MAPPING -->
    <servlet-mapping>
        <servlet-name>Resteasy</servlet-name>

        <url-pattern>/deal/*</url-pattern>
    </servlet-mapping>


    <servlet-mapping>
        <servlet-name>SpringMVC</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>securedapp</web-resource-name>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <user-data-constraint>
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
    </security-constraint>
</web-app>                                              

My LoginLogoutServiceImpl is

    @Service
    @Path("/loginService")
    public class LoginLogoutServiceImpl extends BaseService {

        @Autowired
        private UserDetailsService userService;

        @Autowired
        @org.springframework.beans.factory.annotation.Qualifier("authenticationManager")
        private AuthenticationManager authManager;
/**
     * Authenticates a user and creates an authentication token.
     * 
     * @param username
     *            The name of the user.
     * @param password
     *            The password of the user.
     * @return A transfer containing the authentication token.
     */
    @Path("authenticate")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public TokenTransfer authenticate(@FormParam("username") String username,
            @FormParam("password") String password) {
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
                username, password);
        Authentication authentication = this.authManager
                .authenticate(authenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);

        /*
         * Reload user as password of authentication principal will be null
         * after authorization and password is needed for token generation
         */
        UserDetails userDetails = this.userService.loadUserByUsername(username);

        return new TokenTransfer(TokenUtils.createToken(userDetails));
    }
}

When I run application it gives error like

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [org.springframework.security.core.userdetails.UserDetailsService] is defined: expected single matching bean but found 2: [loginLogoutBusinessServiceImpl, userDao]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:800)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:707)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:478)
    ... 25 more                                                                                                                                                                                                                                                                                                                                                                     

Upvotes: 0

Views: 2353

Answers (1)

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [org.springframework.security.core.userdetails.UserDetailsService] is defined: expected single matching bean but found 2: [loginLogoutBusinessServiceImpl, userDao]

Looking at the error it seems there are two beans loginLogoutBusinessServiceImpl and userDao both referring implementing of the interface UserDetailsService.

Either two different classes (both spring managed) are implementing UserDetailsService or single class implementing it but configured twice as two different beans with Spring.

So spring is not able to decide which needs to be injected.

Use @Qualifer annotation in LoginLogoutServiceImpl to tell spring which one needs to be injected.

Ex:

@Autowired()
@Qualifier("loginLogoutBusinessServiceImpl") or @Qualifier("userDao")
private UserDetailsService userService;

Upvotes: 1

Related Questions