通过Spring Repository和Service获取数据库而不使用Session

时间:2015-08-13 10:06:26

标签: spring spring-mvc spring-security spring-data-jpa restful-authentication

由于K. Potgieter's implementation,我的Spring应用程序中已经有了一个身份验证过滤器。但是,到目前为止,过滤器仅使用不连接到任何数据库但只返回硬编码值的存储库。我现在正试图从数据库中获取用户详细信息(名称和API密钥)。

问题是,每当我从服务类自动装配存储库时,autowire就会失败。我依赖Spring-data-REST的存储库,因为我希望将来还能从REST API中公开用户详细信息:

@RepositoryRestResource(collectionResourceRel = "users", path = "users")
public interface UserSecurityRepository extends CrudRepository<Users, Long> {
    public Users findByUsername(@Param("uname") String name);    
    public Users findByApiKey(@Param("key") String apiKey);    
}
来自身份验证提供程序的

我称之为服务:

import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
public class RESTDaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
    @Override
    protected UserDetails retrieveUser(String apiKey, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        UserDetails loadedUser;
        try {
            loadedUser = this.getUserSecurityService().loadUserByApiKey(apiKey);
        } catch (UsernameNotFoundException | UserNotFoundException notFound) {
            throw notFound;
        } catch (Exception repositoryProblem) {
            throw new AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
        }
        if (loadedUser == null) {
            throw new AuthenticationServiceException(
                    "UserSecurityServiceImpl returned null, which is an interface contract violation");
        }
        return loadedUser;
    }

然后,服务调用存储库(上面粘贴的代码)以通过数据库中的API密钥获取用户:

import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
@Service
@Transactional
public class UserSecurityServiceImpl extends UserDetailsService {
 @Autowired
    private UserSecurityRepository userSecurityRepository;
    @Override
    public UserDetails loadUserByApiKey(String apiKey) throws UserNotFoundException {
UserDetails userDetails = buildUserDetails(userSecurityRepository.findByApiKey(apiKey));
        if (userDetails == null) {
            throw new UserNotFoundException("User could not be found with the supplied api key.");
        }
        return userDetails;
    }

但是,此提取失败,因为来自服务类的存储库的自动装配失败。我收到这个错误:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userSecurityServiceImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private mypackage.dal.UserSecurityRepository mypackage.services.UserSecurityServiceImpl.userSecurityRepository; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [mypackage.dal.UserSecurityRepository] 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)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]
...

我不会使用Session对象访问数据库,因为我希望我的应用程序是无状态的,并且我在security.xml sec中声明:session-management session-fixation-protection =&#34; none&#34 ; 如你所见:

<?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:sec="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/security
       http://www.springframework.org/schema/security/spring-security-4.0.xsd">
    <sec:http disable-url-rewriting="true" entry-point-ref="forbiddenEntryPoint" use-expressions="true" create-session="never">
        <sec:anonymous enabled="false"/>
        <sec:session-management session-fixation-protection="none"/>
        <sec:custom-filter ref="restAuthenticationFilter" position="FORM_LOGIN_FILTER"/>
        <sec:intercept-url pattern="/**" access="isFullyAuthenticated()"/>
    </sec:http>    
    <bean id="forbiddenEntryPoint" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint"/>    
    <sec:authentication-manager alias="defaultAuthenticationManager" erase-credentials="true">
        <sec:authentication-provider ref="daoAuthenticationProvider"/>
    </sec:authentication-manager>    
    <bean id="daoAuthenticationProvider" class="mypackage.dal.security.RESTDaoAuthenticationProvider">
        <property name="userSecurityService" ref="userSecurityServiceImpl"/>
        <property name="passwordEncoder" ref="passwordEncoder"/>
    </bean>    
    <bean id="passwordEncoder" class="mypackagedal.security.authentication.HMacShaPasswordEncoder">
        <constructor-arg name="strength" value="256"/>
        <constructor-arg name="encodeHashAsBase64" value="true"/>
    </bean>    
    <bean id="restAuthenticationFilter" class="mypackage.dal.security.RESTAuthenticationFilter">
        <constructor-arg name="defaultFilterProcessesUrl" value="/"/>
        <property name="authenticationManager" ref="defaultAuthenticationManager"/>
        <property name="authenticationSuccessHandler">
            <!-- Upon successful authentication, Spring will attempt to try and move you to another URL -->
            <!-- We have to prevent this because the request for the resource and the authentication all get done in the same request! -->
            <bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler">
                <property name="redirectStrategy">
                    <bean class="mypackage.dal.security.NoRedirectStrategy"/>
                </property>
            </bean>
        </property>
    </bean>
</beans>

那么,如何使用Repository and Service从数据库中获取这些用户数据而没有会话对象呢?

非常感谢能帮助我的人。

P.S。 我使用的是Hibernate,Liquibase和Hsqldb。我的申请背景如下:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                        http://www.springframework.org/schema/tx
                        http://www.springframework.org/schema/tx/spring-tx-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/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">


    <context:component-scan base-package="mypackage" />
    <context:annotation-config />

    <tx:annotation-driven transaction-manager="transactionManager" />
    <aop:config proxy-target-class="true" />
    <!--<mvc:annotation-driven/>-->

    <bean class="org.springframework.web.servlet.view.UrlBasedViewResolver">
        <property name="suffix" value=".jsp"/>
        <property name="prefix" value="/WEB-INF/views/"/>
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
    </bean>

    <import resource="security-context.xml"/>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
          destroy-method="close">
        <property name="driverClassName" value="org.hsqldb.jdbc.JDBCDriver" />
        <property name="url" value="jdbc:hsqldb:hsql://localhost/sdrdb" />
        <property name="username" value="SA" />
        <property name="password" value="" />
    </bean>

    <bean id="sessionFactory"
              class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <!--    <bean id="sessionFactory" 
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" 
        depends-on="flywayAutomaticMigrationBean">-->
        <property name="dataSource" ref="dataSource" />
        <property name="packagesToScan" value="mypackage.dal.entities" />
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.connection.pool_size">10</prop>
                <prop key="hibernate.connection.show_sql">true</prop>
                <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
            </props>
        </property>
    </bean>

    <bean id="transactionManager"
              class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
    <!--    <bean id="transactionManager"
          class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean> -->

</beans>

ATTEMPT 2: 这次我试图不再使用RestRepository而是使用Spring Data JPA。但是,在注入依赖项时,我仍然会收到NoSuchBeanDefinitionException。现在我的存储库如下:

@Repository
public class JpaUserSecurityRepositoryImpl {

    @PersistenceContext
    private EntityManager em;


    public Users findByUsername(String name) {
        Query query = this.em.createQuery("SELECT users FROM Users WHERE name =:name");
        query.setParameter("name", name);
        return (Users) query.getSingleResult();
    }

    public Users findByApiKey(String apiKey) {
        Query query = this.em.createQuery("SELECT users FROM Users WHERE api_key =:key");
        query.setParameter("key", apiKey);
        return (Users) query.getSingleResult();
    }

}

我的@Service访问存储库,如下所示:

@Service
public class UserSecurityServiceImpl implements UserSecurityService {


    private UserSecurityRepository repository;


    @Autowired
    public UserSecurityServiceImpl(UserSecurityRepository repository){
        this.repository = repository;
    }

我收到NoSuchBeanDefinitionException错误:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaUserSecurityRepositoryImpl': Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined
    at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.postProcessPropertyValues(PersistenceAnnotationBeanPostProcessor.java:357) ~[spring-orm-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1210) ~[spring-beans-4.1.6.RELEASE.jar:4.1.6.RELEASE]

尝试3:

这次我放弃了没有创建会话的愿望,所以我尝试使用存储库中的SessionFactory。我在security.xml中注释掉了

的行
<!--<sec:session-management session-fixation-protection="none"/>-->

从我的服务我访问以下自动装配的存储库(autowire工作):

@Repository
public class DataProviderImpl {

    @Autowired
    private SessionFactory sessionFactory;

    @Transactional
    public List<Users> getUsers() {
        Session session = sessionFactory.openSession();
        //I also tried: Session session = sessionFactory.getCurrentSession();

        Query query = session.createQuery("from Users u");
        return query.list();
    }

    public Users findByUsername(String name) {
        List<Users> allusers = getUsers();
        for (Users user : allusers) {
            if (user.getFirstName().equals(name)) {
                return user;
            }
        }
        return null;
    }

请注意 会话会话= sessionFactory.openSession(); //我也尝试过:Session session = sessionFactory.getCurrentSession(); 从上面的代码。

它编译和部署但是当我打开一个页面,而不是然后获得身份验证失败:错误的密钥等我得到身份验证失败:当我尝试sessionFactory.openConnection()时无法打开连接 我得到了 身份验证失败:没有Hibernate会话绑定到线程,并且配置不允许在此处创建非事务性 当我尝试Session session = sessionFactory.getCurrentSession();来自存储库。

我真的很想设法以某种方式从我的数据库中获取这些数据。任何帮助都将受到超级赞赏!万分感谢

1 个答案:

答案 0 :(得分:1)

根据您的第一次尝试,default-spring-context.xml的内容是什么?组件扫描是否包含您的包装?我原本认为@RepositoryRestResource声明会让你可以使用这个bean,但我之前没有使用过spring-data-REST,所以我不确定。