views:

879

answers:

3

I'm trying to integrate Spring Security with Hibernate. I'm new to both technologies so I'm almost certainly taking too many steps at once here, but I'm at the point where I want to authenticate a user from the database. I think this is certainly more a Hibernate problem that a Spring Security one but I mention it to give some context. Below is the error message and the code. Can anyone spot something?

org.hibernate.HibernateException: No session currently bound to execution context
    org.hibernate.context.ManagedSessionContext.currentSession(ManagedSessionContext.java:74)
    org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:622)
    com.vicinity.dao.hibernate.GenericHibernateDAO.findByCriteria(GenericHibernateDAO.java:99)
    com.vicinity.dao.hibernate.HibernateUserDAO.getUserByLogin(HibernateUserDAO.java:35)
    com.vicinity.service.PersistentUserManager.loadUserByUsername(PersistentUserManager.java:67)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    java.lang.reflect.Method.invoke(Unknown Source)
    org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:307)
    org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:107)
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    $Proxy31.loadUserByUsername(Unknown Source)
    org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser(DaoAuthenticationProvider.java:83)
    org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:125)
    org.springframework.security.authentication.ProviderManager.doAuthentication(ProviderManager.java:121)
    org.springframework.security.authentication.AbstractAuthenticationManager.authenticate(AbstractAuthenticationManager.java:49)
    org.springframework.security.authentication.ProviderManager.doAuthentication(ProviderManager.java:139)
    org.springframework.security.authentication.AbstractAuthenticationManager.authenticate(AbstractAuthenticationManager.java:49)
    org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:98)
    org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:356)
    org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:106)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:356)
    org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:356)
    org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:108)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:356)
    org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:150)
    org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
    org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)

Configuration of the DAO and transaction management:

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
 <property name="configLocation">
  <value>classpath:hibernate.cfg.xml</value>
 </property>
</bean>

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

<bean id="hibernateUserDAO" class="com.vicinity.dao.hibernate.HibernateUserDAO">
 <property name="sessionFactory" ref="sessionFactory" />
</bean>

<bean id="userManagerTarget" class="com.vicinity.service.PersistentUserManager">
 <property name="userDAO" ref="hibernateUserDAO" />
</bean> 

<bean id="userManager" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
 <property name="transactionManager">
  <ref local="transactionManager" />
 </property>
 <property name="target">
  <ref local="userManagerTarget" />
 </property>
 <property name="transactionAttributes">
  <props>
   <prop key="loadUserByUsername">PROPAGATION_REQUIRED</prop>
  </props>
 </property>
</bean>

Here is the class that implements the UserDetailsService which is called to authenticate the user. This makes a call to the DAO, see the line userDAO.getUserByLogin(login);:

@Service("userManager")
@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
public class PersistentUserManager implements UserManager, UserDetailsService {

        @Override
        public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException, DataAccessException {
            User user = userDAO.getUserByLogin(login);

            if (null == user) {
                logger.error("User with login: " + login + " not found in database");
                throw new UsernameNotFoundException("user not found in database");
            }

            org.springframework.security.core.userdetails.User springUser;

            springUser = new org.springframework.security.core.userdetails.User(user.getLogin(), user.getPassword(), true,
                    true, true, true, new ArrayList<GrantedAuthority>());

            return springUser;
        }
    }

Here is the DAO which reads from the database. Note that I'm trying to take advantage of the 'GenericDAO pattern':

@Repository("userDAO")
public class HibernateUserDAO extends GenericHibernateDAO<User, Long> implements UserDAO {

    public HibernateUserDAO() {
        super(User.class);
    }

    @Override
    public void createUser(User user) {
        super.makePersistent(user);
    }

    public User getUserByLogin(String login) {
        if (null == login) {
            throw new IllegalArgumentException("You must provide a username if you want to get the user.");
        }

        List<User> users = findByCriteria(Restrictions.eq("login", login));

        // TODO, might need to check here if there are more than one user with the same username
        if(users == null || users.size() == 0) {
            return null;
        } else {
            return users.get(0);
        }
    }
}

Here's the class where the error occurs, the line Criteria crit = getSessionFactory().getCurrentSession().createCriteria(getPersistentClass());:

public abstract class GenericHibernateDAO<T, ID extends Serializable> implements GenericDAO<T, ID> {

    private Class<T> persistentClass;

    private SessionFactory sessionFactory;

    @SuppressWarnings("unchecked")
    protected List<T> findByCriteria(Criterion... criterion) {
        Criteria crit = getSessionFactory().getCurrentSession().createCriteria(getPersistentClass());
        for (Criterion c : criterion) {
            crit.add(c);
        }
        return crit.list();
    }
}

I have the following in my hibernate configuration (hibernate.cfg.xml) which may be relavent:

<property name="current_session_context_class">org.hibernate.context.ManagedSessionContext</property>
A: 

Have you configured transaction management?

org.springframework.orm.hibernate3.HibernateTransactionManager

and

org.springframework.transaction.interceptor.TransactionProxyFactoryBean
rodrigoap
No. But I have now and edited my original post. Same error but I can see the AOP kicking in at least. Can you take a look?
chrisjleu
A: 

Try with this property name.

<property name="hibernate.current_session_context_class">
       org.hibernate.context.ManagedSessionContext</property>

However, it is preferable that you configure spring to be in charge of your session and transaction management. You can check this.

Bozho
Ok I've had a go at configuring transaction management and I also changed the name of the property like you suggested but have had no success yet.
chrisjleu
A: 

I think the problem is coming due to
springUser = new org.springframework.security.core.userdetails.User(user.getLogin(), user.getPassword(), true, true, true, true, new ArrayList());

Spring uses a Template pattern in the DAO, so when you do getUserByLogin a session is created by Spring and when you come out of this method the session is closed.So when you do user.getLogin() again there is no session attached and you get the error.

Try to use OpenSessionInViewFilter.... you might have to search in google for it (as i have also not used it myself, hence cannot show code here)

Also you can do one more thing, if you return the springUser from the DAO itself (i.e in getUserByLogin itself, then it will use the same session object and it should work)

Rajat
Thanks for the response but I think this answer is way off the mark. Firstly, the line of code you refer to doesn't even get executed. Secondly I'm not using Spring's template pattern (I recently read that this has become largely redundant especially when using Hibernate 3). Thirdly, my application's representation of a User is different from Spring Security's and as far as I understand it currently it is necessary to provide a ` org.springframework.security.core.userdetails.User1` to the Spring Security framework.
chrisjleu
Yes you are right, I didnot see earlier that you are not using the Spring template pattern.
Rajat

related questions