【问题标题】:Unable to autowire a field in UserDetailsService无法自动装配 UserDetailsS​​ervice 中的字段
【发布时间】:2014-08-22 05:05:21
【问题描述】:

我知道有一些关于这个话题的问题,但我的有点不同。我正在尝试将 openID 身份验证包含在使用 Spring、Spring-security 和 Spring-MVC 开发的项目中。

要实现openID auth,需要一些类:在applicationContext-security.xml中配置AccessDeniedHandler和UserDetailsS​​ervice:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:security="http://www.springframework.org/schema/security"
    xmlns="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.1.xsd
                        http://www.springframework.org/schema/security
                         http://www.springframework.org/schema/security/spring-security-3.1.xsd">

    <!-- turn on global security -->
    <security:global-method-security secured-annotations="enabled"/>

    <bean id="openIdAuthFailureHandler" class="es.institution.dept.security.MyAccessDeniedHandler"/>
    <bean id="userDetailsService" class="es.institution.dept.service.impl.UserDetailsServiceImpl"/>

    <security:http auto-config="true">
        <security:intercept-url pattern="/welcome*" access="ROLE_USER, ROLE_ADMIN" />
        <security:intercept-url pattern="/user/*" access="ROLE_USER, ROLE_ADMIN" />
        <security:intercept-url pattern="/rest/*" access="ROLE_USER, ROLE_ADMIN" />
        <security:intercept-url pattern="/admin/*" access="ROLE_ADMIN" />
        <security:logout logout-success-url="/" />
        <security:openid-login login-page="/openidLogin" default-target-url="/welcome" authentication-failure-url="/loginfailed" user-service-ref="userDetailsService"/>
        <security:access-denied-handler ref="openIdAuthFailureHandler"/>
    </security:http>        

    <security:authentication-manager>
       <security:authentication-provider>
        <security:password-encoder hash="md5"/>
        <security:jdbc-user-service data-source-ref="dataSource"
           users-by-username-query="
              SELECT username, password, active as enabled  
              FROM users WHERE username=?"  

           authorities-by-username-query="
              select ur.username, ur.rolename as authority from users_roles ur 
              where ur.username=?" />
       </security:authentication-provider>
    </security:authentication-manager>
</beans>

Spring 在需要知道用户数据(用户名、密码、角色...)时调用 UserDetailsS​​ervice。因此,我需要在 UserDetailsS​​ervice 中调用我的服务之一(UserService):

public class UserDetailsServiceImpl implements UserDetailsService{

    @Autowired
    UserService userService;

    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        es.institution.dept.model.User user = userService.getUserByUsername("mannuk");
        if(user == null)
            throw new UsernameNotFoundException("User does not exist");
        return new User(user.getUsername(), user.getPassword(), user.isActive(), false, false, false, getGrantedAuthorities(username));
    }

    public List<GrantedAuthority> getGrantedAuthorities(String username) {
        List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
        for (Role role : userService.getAllRoles(username)) {
            authorities.add(new SimpleGrantedAuthority(role.getRoleName()));
        }
        return authorities;
    }
}

我尝试了两种选择:

1) 在 UserDetailsS​​ervice 中定义 @Service 注解,它会在启动期间抛出异常。它说 UserDetails bean 不存在(在 applicationSecurity-context.xml 中是必需的)

2) 在 applicationContext-security.xml 中声明一个 bean 定义。启动正常(没有错误),但 UserService 没有自动装配。

这是我的 applicationContext.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:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

    <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiName" value="java:comp/env/jdbc/adminDB"/>
    </bean>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation" value="/WEB-INF/mybatis-config.xml" /> 
    </bean> 

    <bean id="usersMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="mapperInterface" value="es.institution.dept.dao.UserMapper" />
        <property name="sqlSessionFactory" ref="sqlSessionFactory" />
    </bean> 

    <bean id="rolesMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="mapperInterface" value="es.institution.dept.dao.RoleMapper" />
        <property name="sqlSessionFactory" ref="sqlSessionFactory" />
    </bean> 

    <bean id="groupMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="mapperInterface" value="es.institution.dept.dao.GroupMapper" />
        <property name="sqlSessionFactory" ref="sqlSessionFactory" />
    </bean> 

    <bean id="policyMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="mapperInterface" value="es.institution.dept.dao.PolicyMapper" />
        <property name="sqlSessionFactory" ref="sqlSessionFactory" />
    </bean> 

    <!-- Json converter bean --> 
    <bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
        <property name="objectMapper" ref="jacksonObjectMapper" />
    </bean>

    <bean id="jacksonObjectMapper" class="org.codehaus.jackson.map.ObjectMapper"></bean>
</beans>

这是我的 app-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:task="http://www.springframework.org/schema/task"
 xmlns:mvc="http://www.springframework.org/schema/mvc"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
  http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
  http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd">

    <!-- Enabling Spring beans auto-discovery -->
    <context:component-scan base-package="es.institution.dept" />

    <!-- Enabling Spring MVC configuration through annotations -->
    <mvc:annotation-driven />

    <!-- Enabling Spring Async tasks through annotations -->
    <task:annotation-driven />

    <mvc:view-controller path="/" view-name="login" />

    <!-- Load resources -->
    <mvc:resources mapping="/resources/**" location="/resources/"/>

    <!-- Bean definitions i18n -->

    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
        <property name="defaultLocale" value="en" />
    </bean>

    <!--  Intercepts the change of the locale: example.html?ln=en -->
    <bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
        <property name="paramName" value="ln" />
    </bean>

    <bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping" >
        <property name="interceptors">
           <list>
            <ref bean="localeChangeInterceptor" />
            </list>
        </property>
    </bean>

    <!-- Register the messages.properties -->
    <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basename" value="WEB-INF/classes/locale/messages" />
    </bean>

    <!-- Defining which view resolver to use -->
    <bean class= "org.springframework.web.servlet.view.InternalResourceViewResolver" > 
        <property name="prefix" value="/WEB-INF/views/" /> 
        <property name="suffix" value=".jsp" /> 
    </bean>
</beans>

请注意,UserService 在控制器等其他地方运行良好。这似乎是 UserDetailsS​​ervice 本身的问题。

如果您需要更多信息,请随时给我写信。我希望能解决这个问题。解决方案将被投票和检查。

【问题讨论】:

    标签: spring spring-mvc spring-security


    【解决方案1】:
    • 在根应用程序上下文(applicationContext.xmlapplicationContext-security.xml)中声明的 Bean 无法访问在特定于 servler 的上下文(app-servlet.xml)中声明的 bean
    • Spring Security 的组件(包括UserDetailsService)必须在根应用上下文中声明

    因此,您需要在applicationContext.xml 中声明UserService,而不是在app-servlet.xml 中由&lt;context:component-scan&gt; 提取。

    【讨论】:

    • 是否有可能以另一种方式实现?我的意思是...在另一个应用程序中,我在 appContext (AutorizationManager) 中声明了一个 bean,它自动装配服务。也许,在 servlet 中我只需要扫描 es.institution.dept.controller。如果我在 appContext 中声明 bean UserService,则其字段不会自动装配。你能澄清一下吗?
    • 如果UserService依赖于app-servlet.xml中的其他bean,则可以将app-servlet.xml的所有内容移动到applicationContext.xml。据我记得,它应该可以在没有其他更改的情况下工作。
    • UserService 不依赖于 app-servlet.xml 中的 bean。 UserService 依赖于我在 appContext.xml 中声明的 DAO bean。像 UserController、AdminController 这样的控制器 bean 依赖于 UserService。如果我将 es.institution.dept 更改为 es.institution.dept.controller,则从 Controller bean 启动时会出现异常:无法自动装配字段:es.institution.dept.service.UserService es.institution.dept.controller。 AdminController.userService;。我需要在 appContext 中包含另一个组件扫描吗?
    • 如果是这样,您可以从UserService 中删除@Service(将其从组件扫描中排除)并在appContext 中手动声明。
    猜你喜欢
    • 2016-03-29
    • 1970-01-01
    • 2015-09-14
    • 1970-01-01
    • 2014-02-26
    • 2019-11-04
    • 1970-01-01
    • 1970-01-01
    • 2015-03-19
    相关资源
    最近更新 更多