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>