为什么我会收到以下错误? "No unique bean of type is defined: expected single matching bean but found 3"



我们使用Eclipse Kepler IDE开发GWT应用程序,并且长期以来一直使用Ant脚本部署到Tomcat容器。为了简化GWT的编译和部署过程,我们最近决定改用Hudson CI服务器。我们遇到的问题是,当我们允许Eclipse进行自己的连续构建,然后执行GWT编译/部署时,我们的应用程序可以按预期工作。然而,当我们使用外部javac进程进行编译时,无论是从独立的JDK还是使用Eclipse的编译器(org.eclipse.jdt.core.JDTCompilerAdapter),我们都会收到以下错误消息:

Error creating bean with name 'loginServiceImpl' defined in file [/opt/tomcat/webapps/ROOT##10616/WEB-INF/classes/com/company/ribeye/server/service/LoginServiceImpl.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [org.springframework.security.authentication.AuthenticationManager]: : No unique bean of type [org.springframework.security.authentication.AuthenticationManager] is defined: expected single matching bean but found 3: [org.springframework.security.config.authentication.AuthenticationManagerFactoryBean#0, org.springframework.security.authentication.ProviderManager#0, org.springframework.security.authenticationManager]; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [org.springframework.security.authentication.AuthenticationManager] is defined: expected single matching bean but found 3: [org.springframework.security.config.authentication.AuthenticationManagerFactoryBean#0, org.springframework.security.authentication.ProviderManager#0, org.springframework.security.authenticationManager]

我们没有使用任何类型的依赖管理器,但Eclipse似乎可以毫无问题地处理java编译,而且我们没有遇到这个错误。我想强调的是,只有当我们使用Ant任务和javac进行编译时,才会出现这种错误。同样,即使在使用Eclipse自己的捆绑编译器时,我们也会遇到问题。我还想补充一点,我不是Java开发人员,但我是我们开发团队的DevOps经理,因此CI服务器和部署策略属于我的领域。我有一点Java知识,而且还在学习中,所以如果这是一件非常简单的事情,我提前道歉。

以下是我的相关代码(为了简洁起见,没有列出所有导入):

package com.company.ribeye.server.service;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
@Controller
@RequestMapping("/login")
public class LoginServiceImpl extends GWTController implements LoginService {
    private static final long serialVersionUID = 1L;
    private final AuthenticationManager authenticationManager;
    private final UserDao userDao;
    private final UserPreferenceDao userPreferenceDao;
    @Autowired
    public LoginServiceImpl(AuthenticationManager authenticationManager, UserDao userDao, UserPreferenceDao userPreferenceDao) {
        this.authenticationManager = authenticationManager;
        this.userDao = userDao;
        this.userPreferenceDao = userPreferenceDao;
    }
    @Override
    public ApplicationData getApplicationData() {
        ApplicationData ap = new ApplicationData();
        SimpleDateFormat df = new SimpleDateFormat("yyyy");
        ap.setCurrentYear(Integer.parseInt(df.format(new Date())));
        ap.setLoggedIn(false);
        if (!ServerContext.isProductionServer() && ServerContext.isDevServer()) {
            Pair<String, String> login = ServerContext.getDevAutoLogin();
            ap.setDevModeAutoUsername(login.getA());
            ap.setDevModeAutoPassword(login.getB());
        }
        if (ServerContext.getSession().getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY) == null) {
            return ap;
        }
        User user = ServerContext.getCurrentUser();
        if (user == null) {
            return ap;
        }
        ApplicationContext ctx = ServerContext.getApplicationContext();
        if (user.isPortalUser()) {
            CustomerDao customerDao = (CustomerDao) ctx.getBean("customerDaoImpl");
            user.setCustomer(customerDao.getById(user.getCustomerId()));
        } else if (user.isVendorUser()) {
            VendorDao vendorDao = (VendorDao) ctx.getBean("vendorDaoImpl");
            user.setVendor(vendorDao.getById(user.getVendorId()));
        }
        ap.setLoggedIn(true);
        ap.setDashDatabaseConnection(DashDataSourceContextHolder.getDatabaseType());
        ap.setBuildNumber(ServerContext.getBuildNumber());
        ap.setBuildDate(ServerContext.getBuildDate());
        ap.setNextReleaseDate(ServerContext.getNextReleaseDate());
        ap.setCurrentUser(user);
        ap.setUserLinks(userPreferenceDao.getUserLinks(user.getId()));
        ap.setMyContacts(userPreferenceDao.getMyContacts());
        if (!user.isSwitched()) {
            UserDaoImpl.updateUserActivity(user.getId());
        }
        ap.setUserActivity(UserDaoImpl.getAllUserActivity());
        SystemDao systemDao = (SystemDao) ctx.getBean("systemDaoImpl");
        ap.setEntityLinkers(systemDao.getEntityLinkers());
        PollUpdateData pud = new PollUpdateData(user.getId());
        ap.setNotificationSummary(systemDao.getPollData(pud).getNotificationSummary());
        ap.setNotificationEntityTypeColors(systemDao.getNotificationEntityTypeColors());
        ap.setUserPreferences(userPreferenceDao.getPreferencesByGroupName(user.getId(), null));
        ArgMap<FlagArg> flagArgs = new ArgMap<FlagArg>();
        flagArgs.put(FlagArg.USER_ID, user.getId());
        ap.setFlagDefinitions(Common.asArrayList(userPreferenceDao.getFlagDefinitions(flagArgs)));
        DocumentDao documentDao = (DocumentDao) ctx.getBean("documentDaoImpl");
        ap.setLogoImage(documentDao.getLogoImage());
        return ap;
    }

以下是我们的applicationContext-security.xml的相关部分:

<authentication-manager alias="authenticationManager">
    <authentication-provider ref="ldapAuthProvider" />
    <authentication-provider user-service-ref="customUserDetailsService">
        <password-encoder ref="passwordEncoder">
            <salt-source ref="saltSource" />
        </password-encoder>
    </authentication-provider>
</authentication-manager>

<beans:bean id="ldapAuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<beans:constructor-arg>
    <beans:bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
    <beans:constructor-arg ref="contextSource" />
    <beans:property name="userSearch">
        <beans:bean id="userSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
            <beans:constructor-arg index="0" value="" />
            <beans:constructor-arg index="1" value="REDACTED" />
            <beans:constructor-arg index="2" ref="contextSource" />
        </beans:bean>
    </beans:property>
    </beans:bean>
</beans:constructor-arg>
<beans:constructor-arg>
    <beans:bean class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
         <beans:constructor-arg ref="contextSource"/>
         <beans:constructor-arg value="REDACTED" />
         <beans:property name="groupRoleAttribute" value="cn" />
         <beans:property name="searchSubtree" value="false" />
         <beans:property name="convertToUpperCase" value="true" />
         <beans:property name="rolePrefix" value="ROLE_" />
         <beans:property name="groupSearchFilter" value="member={0}" />
         <beans:property name="defaultRole" value="ROLE_DASH" />
    </beans:bean>
</beans:constructor-arg>

和dispatcher-servlet.xml:

<?xml version="1.0" encoding="UTF-8"?>
<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"
       xmlns:security="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.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.ribeye.server.service" />
  <context:component-scan base-package="com.company.ribeye.server.dao" />
  <context:component-scan base-package="com.company.ribeye.server.util" />
  <context:annotation-config />
  <security:global-method-security pre-post-annotations="enabled" />
</beans>

customUserDetailsService,根据请求:

package com.company.ribeye.server.spring;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
public class CustomUserDetailsService implements UserDetailsService {
    private DataSource dataSource;
    private JdbcTemplate sjt;
    public DataSource getDataSource() {
        return dataSource;
    }
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        RowMapper<User> mapper = new RowMapper<User>() {
            @Override
            public User mapRow(ResultSet rs, int rowNum) throws SQLException {
                return new User(rs.getString("Login"), rs.getString("PasswordDigest"), rs.getBoolean("IsEnabled"), rs.getBoolean("IsEnabled"), true, true,
                        getAuthorities(rs));
            }
        };
        User user;
        String sql = "select top 1 u.ID, u.Login, u.PasswordDigest, u.CustomerID, u.VendorID, u.IsPortalSuperUser, ";
        sql += "dbo.IsActive(u.StartDate, u.EndDate, getdate()) as IsEnabled from Users u ";
        sql += "where u.Login = ? and PasswordDigest is not null";
        try {
            user = sjt.queryForObject(sql, mapper, username);
        } catch (DataAccessException e) {
            throw new UsernameNotFoundException(username);
        }
        return user;
    }
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
        sjt = new JdbcTemplate(dataSource);
    }
    private List<GrantedAuthority> getAuthorities(ResultSet rs) throws SQLException {
        final List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();
        if (rs.getInt("VendorID") > 0) {
            authList.add(new SimpleGrantedAuthority("ROLE_COMPANY_DASH_USERS"));
            return authList;
        }
        authList.add(new SimpleGrantedAuthority("ROLE_PORTAL_USER"));
        if (rs.getBoolean("IsPortalSuperUser")) {
            authList.add(new SimpleGrantedAuthority("ROLE_PORTAL_SUPERUSER"));
            authList.add(new SimpleGrantedAuthority("ROLE_PORTAL_BILLING"));
            authList.add(new SimpleGrantedAuthority("ROLE_PORTAL_OPERATIONS"));
            return authList;
        }
        // if not a superuser, fetch account-level roles from database
        RowMapper<List<GrantedAuthority>> mapper = new RowMapper<List<GrantedAuthority>>() {
            @Override
            public List<GrantedAuthority> mapRow(ResultSet rs, int rowNum) throws SQLException {
                if (rs.getInt("Billing") > 0) {
                    authList.add(new SimpleGrantedAuthority("ROLE_PORTAL_BILLING"));
                }
                if (rs.getInt("Operations") > 0) {
                    authList.add(new SimpleGrantedAuthority("ROLE_PORTAL_OPERATIONS"));
                }
                return authList;
            }
        };
        String sql = "select sum(cast(Billing as int)) as Billing, sum(cast(Operations as int)) as Operations ";
        sql += "from PortalUserRoles ";
        sql += "where UserID = ?";
        sjt.queryForObject(sql, mapper, rs.getInt("ID"));
        return authList;
    }
}

经过多次搜索,我找到了#spring(freenode)上有人给我的解决方案。我仍然不知道为什么这在Eclipse内部不是一个问题,但至少它现在可以工作了。

虽然我在我们的应用程序中找不到其他两个bean的创建位置,但向LoginServiceImpl添加@Qualifier("authenticationManager")注释解决了问题:

@Autowired
    public LoginServiceImpl(@Qualifier("authenticationManager") AuthenticationManager authenticationManager, UserDao userDao,
            UserPreferenceDao userPreferenceDao) {
        this.authenticationManager = authenticationManager;
        this.userDao = userDao;
        this.userPreferenceDao = userPreferenceDao;
    }

相关内容

最新更新