Spring Security + Hibernate Annotation Example

spring-hibernate-logo

In this tutorial, previous Spring Security + Hibernate4 XML example will be reused, and convert it to a annotation-based example.

Technologies used :

  1. Spring 3.2.8.RELEASE
  2. Spring Security 3.2.3.RELEASE
  3. Hibernate 4.2.11.Final
  4. MySQL Server 5.6
  5. Tomcat 7 (Servlet 3.x container)

Quick Note :

  1. Create a session factory with LocalSessionFactoryBuilder
  2. Inject session factory into a UserDao
  3. Integrate UserDao into a custom UserDetailsService, to load users from the database.

1. Project Directory

A final project directory structure.

spring-security-hibernate-annotation-directory

2. User Model + Mapping File

Model classes and its’ annotation-based mapping file.

User.java

package com.mkyong.users.model;

import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "users", catalog = "test")
public class User {

	private String username;
	private String password;
	private boolean enabled;
	private Set<UserRole> userRole = new HashSet<UserRole>(0);

	public User() {
	}

	public User(String username, String password, boolean enabled) {
		this.username = username;
		this.password = password;
		this.enabled = enabled;
	}

	public User(String username, String password, 
		boolean enabled, Set<UserRole> userRole) {
		this.username = username;
		this.password = password;
		this.enabled = enabled;
		this.userRole = userRole;
	}

	@Id
	@Column(name = "username", unique = true, 
		nullable = false, length = 45)
	public String getUsername() {
		return this.username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	@Column(name = "password", 
		nullable = false, length = 60)
	public String getPassword() {
		return this.password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	@Column(name = "enabled", nullable = false)
	public boolean isEnabled() {
		return this.enabled;
	}

	public void setEnabled(boolean enabled) {
		this.enabled = enabled;
	}

	@OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
	public Set<UserRole> getUserRole() {
		return this.userRole;
	}

	public void setUserRole(Set<UserRole> userRole) {
		this.userRole = userRole;
	}

}
UserRole.java

package com.mkyong.users.model;

import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

@Entity
@Table(name = "user_roles", catalog = "test", 
	uniqueConstraints = @UniqueConstraint(
		columnNames = { "role", "username" }))
public class UserRole{

	private Integer userRoleId;
	private User user;
	private String role;

	public UserRole() {
	}

	public UserRole(User user, String role) {
		this.user = user;
		this.role = role;
	}

	@Id
	@GeneratedValue(strategy = IDENTITY)
	@Column(name = "user_role_id", 
		unique = true, nullable = false)
	public Integer getUserRoleId() {
		return this.userRoleId;
	}

	public void setUserRoleId(Integer userRoleId) {
		this.userRoleId = userRoleId;
	}

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "username", nullable = false)
	public User getUser() {
		return this.user;
	}

	public void setUser(User user) {
		this.user = user;
	}

	@Column(name = "role", nullable = false, length = 45)
	public String getRole() {
		return this.role;
	}

	public void setRole(String role) {
		this.role = role;
	}

}

3. DAO Class

DAO classes, to load data from the database, via Hibernate.

UserDao.java

package com.mkyong.users.dao;

import com.mkyong.users.model.User;

public interface UserDao {

	User findByUserName(String username);

}
UserDaoImpl.java

package com.mkyong.users.dao;

import java.util.ArrayList;
import java.util.List;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.mkyong.users.model.User;

@Repository
public class UserDaoImpl implements UserDao {

	@Autowired
	private SessionFactory sessionFactory;

	@SuppressWarnings("unchecked")
	public User findByUserName(String username) {

		List<User> users = new ArrayList<User>();

		users = sessionFactory.getCurrentSession()
			.createQuery("from User where username=?")
			.setParameter(0, username)
			.list();

		if (users.size() > 0) {
			return users.get(0);
		} else {
			return null;
		}

	}

}

4. UserDetailsService

Uses @Transactional to declare a transactional method.

MyUserDetailsService.java

package com.mkyong.users.service;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
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;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.mkyong.users.dao.UserDao;
import com.mkyong.users.model.UserRole;

@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {

	//get user from the database, via Hibernate
	@Autowired
	private UserDao userDao;

	@Transactional(readOnly=true)
	@Override
	public UserDetails loadUserByUsername(final String username) 
		throws UsernameNotFoundException {
	
		com.mkyong.users.model.User user = userDao.findByUserName(username);
		List<GrantedAuthority> authorities = 
                                      buildUserAuthority(user.getUserRole());

		return buildUserForAuthentication(user, authorities);
		
	}

	// Converts com.mkyong.users.model.User user to
	// org.springframework.security.core.userdetails.User
	private User buildUserForAuthentication(com.mkyong.users.model.User user, 
		List<GrantedAuthority> authorities) {
		return new User(user.getUsername(), user.getPassword(), 
			user.isEnabled(), true, true, true, authorities);
	}

	private List<GrantedAuthority> buildUserAuthority(Set<UserRole> userRoles) {

		Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();

		// Build user's authorities
		for (UserRole userRole : userRoles) {
			setAuths.add(new SimpleGrantedAuthority(userRole.getRole()));
		}

		List<GrantedAuthority> Result = new ArrayList<GrantedAuthority>(setAuths);

		return Result;
	}

}

5. Spring Security Annotation

Declares and binds everything with annotations, read the comments, it should be self-explanatory.

SecurityConfig.java

package com.mkyong.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	@Qualifier("userDetailsService")
	UserDetailsService userDetailsService;

	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {

	    http.authorizeRequests().antMatchers("/admin/**")
		.access("hasRole('ROLE_ADMIN')").and().formLogin()
		.loginPage("/login").failureUrl("/login?error")
		.usernameParameter("username")
		.passwordParameter("password")
		.and().logout().logoutSuccessUrl("/login?logout")
		.and().csrf()
		.and().exceptionHandling().accessDeniedPage("/403");
	}
	
	@Bean
	public PasswordEncoder passwordEncoder(){
		PasswordEncoder encoder = new BCryptPasswordEncoder();
		return encoder;
	}
	
}

Uses LocalSessionFactoryBuilder to create a session factory.

AppConfig.java

package com.mkyong.config;

import java.util.Properties;
import org.apache.commons.dbcp.BasicDataSource;
import org.hibernate.SessionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.orm.hibernate4.HibernateTransactionManager;
import org.springframework.orm.hibernate4.LocalSessionFactoryBuilder;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

@EnableWebMvc
@Configuration
@ComponentScan({ "com.mkyong.*" })
@EnableTransactionManagement
@Import({ SecurityConfig.class })
public class AppConfig {

        @Bean
        public SessionFactory sessionFactory() {
                LocalSessionFactoryBuilder builder = 
			new LocalSessionFactoryBuilder(dataSource());
                builder.scanPackages("com.mkyong.users.model")
                      .addProperties(getHibernateProperties());

                return builder.buildSessionFactory();
        }

	private Properties getHibernateProperties() {
                Properties prop = new Properties();
                prop.put("hibernate.format_sql", "true");
                prop.put("hibernate.show_sql", "true");
                prop.put("hibernate.dialect", 
                    "org.hibernate.dialect.MySQL5Dialect");
                return prop;
        }
	
	@Bean(name = "dataSource")
	public BasicDataSource dataSource() {
		
		BasicDataSource ds = new BasicDataSource();
	        ds.setDriverClassName("com.mysql.jdbc.Driver");
		ds.setUrl("jdbc:mysql://localhost:3306/test");
		ds.setUsername("root");
		return ds;
	}
	
	//Create a transaction manager
	@Bean
        public HibernateTransactionManager txManager() {
                return new HibernateTransactionManager(sessionFactory());
        }
		
	@Bean
	public InternalResourceViewResolver viewResolver() {
		InternalResourceViewResolver viewResolver 
                             = new InternalResourceViewResolver();
		viewResolver.setViewClass(JstlView.class);
		viewResolver.setPrefix("/WEB-INF/pages/");
		viewResolver.setSuffix(".jsp");
		return viewResolver;
	}
	
}

Done.

6. Project Demo

The following video demo is for the Spring Security database login tutorial. Since this tutorial is generating the same output, so the video demo is reused.

6.1 Access a password protected page : http://localhost:8080/spring-security-hibernate-annotation/admin , a login page is displayed.

spring-security-hibernate-annotation1

6.2 Enter user “mkyong” and password “123456”.

spring-security-hibernate-annotation2

6.3 Try access /admin page with user “alex” and password “123456”, a 403 page will be displayed.

spring-security-hibernate-annotation3

Download Source Code

References

  1. Spring Security + Hibernate XML Example
  2. Spring Security Hello World Annotation Example
  3. LocalSessionFactoryBuilder JavaDoc
  4. Spring ORM – Hibernate
  5. Spring Hibernate4 LocalSessionFactoryBean JavaDoc
  6. Spring Transaction Management
  7. Hibernate ORM documentation
  8. Spring Security Form Login Using Database, with JDBC
  9. Hibernate : No Session Found For Current Thread

About the Author

author image
mkyong
Founder of Mkyong.com, love Java and open source stuff. Follow him on Twitter. If you like my tutorials, consider make a donation to these charities.

Comments

avatar
37 Comment threads
15 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
44 Comment authors
Luis GomezJDHsunnyjuveriadeepak Recent comment authors
newest oldest most voted
gianluca
Guest
gianluca

but someone has solved the problem of the user name and password always incorrect ?

sora
Guest
sora

hi..i try to follow this tutorial but got error..even when i use
downloaded project also got same error..pls help me im new in
spring..thx

Error creating bean with name ‘securityConfig’: Injection of autowired dependencies failed…
Actually it cant autowired to anything..i think perhaps got problem with @Componentscan and i try to specific the package but it still same..thx

Akash Agarwal
Guest
Akash Agarwal

how did u get rid of this ?

Vamshi Krishna
Guest
Vamshi Krishna

Hi All, I am new to Spring Security. I have a doubt in MyUserDestailsService implementation. In the following method we are creating a User object with 7 parameters but we don’t have such constructor defined. How it is possible? If possible how it will happen and what are the other boolean parameters. Please let me know.
private User buildUserForAuthentication(com.mkyong.users.model.User user,
List authorities) {
return new User(user.getUsername(), user.getPassword(),
user.isEnabled(), true, true, true, authorities);
}

Thanks,
Vamshi

JDH
Guest
JDH

Because this User object is instantiated from the Spring Core framework (org.springframework.security.core.userdetails.User). This constructor is asking 7 parameters as you said:

public User(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection authorities) { .. }

There is also one other (overloaded) method which asks just 3 parameters:

public User(String username, String password, Collection authorities) { .. }

Yogesh Chavan
Guest
Yogesh Chavan

Hello , can someone please explain how password is getting verified here ? As I see the db query is getting the user with only username.

Morteza Malvandi
Guest
Morteza Malvandi

I’m developing an restful application using Spring And Hibernate. I don’t have Login page, I will be very grateful, if you present all of us that How implement this?

Willians Martins
Guest
Willians Martins

Very nice your post, every help us, thanks. Can you help more one time? I want to implement the save method in UserDAOImpl class. But the password is not encrypt. My code:

@Override

public T create(T t) {

Session session = sessionFactory.getCurrentSession();

session.save(t);

session.flush();

return t;

}

Thanks again

Gustavo Rozolin
Guest
Gustavo Rozolin

Mkyong I was following the tutorial, then I get an error and I add a question to stackover flow. http://stackoverflow.com/questions/33205236/spring-security-added-prefix-role-to-all-roles-name

Prabhat Singh
Guest
Prabhat Singh

please share log or exception

perrohunter
Guest
perrohunter

After this I can’t access the tomcat manager at http://localhost:8080/manager/html any ideas on how to add an exception for that path?

metalHead
Guest
metalHead

can’t even build it with maven

Shashank K
Guest
Shashank K

Hi Mkyong, Thanks for the nice tutorial. Works great! I want to some more methods in controller to handle post requests. But I get ”
The specified HTTP method is not allowed for the requested resource (Request method ‘POST’ not supported)” error. Please help to solve this

bill gates
Guest
bill gates

thanks, idiom)): return (User( sessionFactory.getCurrentSession()
.createQuery(“from User where username=?”)
.setParameter(0, username)
.list().get(0);

Stefano
Guest
Stefano

To resolve Invalid username and password
This application use the BCryptPasswordEncoder.
Instead of ‘123456’ use ‘$2a$10$NZ0s/78owi0SSVkNdDcEQ.EPW4u5r.HDSqjXZbOOzYUBc1JrzGbHC’
work for me.
For next pass you must encode that with:
String password = “123456”;
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String hashedPassword = passwordEncoder.encode(password);

GodBlessYou
Guest
GodBlessYou

very nice tutorial..thank you :)

Techie Me
Guest
Techie Me

I have started a new tutorial where I will be developing a production quality J2EE web application from scratch using annotation based Spring, Hibernate, AOP, Transaction Management and many other technologies. Please have a look http://techieme.in/shop-smart-spring-application-setup/

javaCode coder
Guest
javaCode coder

share jsp page

human
Guest
human

hi,

what is the salt. How is the password generated? I want to add registration feature

Alex
Guest
Alex

I’ve made according to this article and i get

jan 13, 2015 12:51:05 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
ERROR: operator does not exist: character varying = bytea

I’m new into hibernate and Spring, but I’ve checked all dependencies, versions and options to find the mistake I’ve made. Still nothing((

shiva
Guest
shiva

i didnt get any solution

shiva
Guest
shiva

When trying to run the application and loading admin page, login
form opens, but, even after supplying username as ‘mkyong’ and password
as ‘123456’. It always displays ‘Invalid username and password’. Not
able to resolve myself even after trying for two days, can somebody help
me.

Stefano
Guest
Stefano

This application use the BCryptPasswordEncoder.
Instead of ‘123456’ use ‘$2a$10$NZ0s/78owi0SSVkNdDcEQ.EPW4u5r.HDSqjXZbOOzYUBc1JrzGbHC’
work for me.
For next pass you must encode that with:
String password = “123456”;
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String hashedPassword = passwordEncoder.encode(password);

Willians Martins
Guest
Willians Martins

Hello Stefano, talking about this, I try to implement the createUser in DAO class and my password is not encrypt, then I try to write like this:

public T create(T t) {
Session session = sessionFactory.getCurrentSession();
User user = (User) t;
user.setPassword(passwordEncoder.encode(user.getPassword()));
session.save(user);
session.flush();
return t;
}

this will save the password encrypt, but when I login, the user and password the message is “invalid user or password”, can you help me?

Willians Martins
Guest
Willians Martins

Resolved! I forgot to set the Role, thanks

Batbold Boldbayar
Guest
Batbold Boldbayar

tnx

Surya
Guest
Surya

I am getting the same error i.e: always displays ‘Invalid username and password’. Please some body help me out

Batbold Boldbayar
Guest
Batbold Boldbayar

just remove passwordEncoder or put $2a$10$NZ0s/78owi0SSVkNdDcEQ.EPW4u5r.HDSqjXZbOOzYUBc1JrzGbHC to your database passwordfield and use 123456 on web password field.

shiva
Guest
shiva

pls give me the solution i also checked db conections and catalog in db

shiva
Guest
shiva

When trying to run the application and loading admin page, login
form opens, but, even after supplying username as ‘mkyong’ and password
as ‘123456’. It always displays ‘Invalid username and password’. Not
able to resolve myself even after trying for two days, can somebody help
me.

Mehrdad Norouzi
Guest
Mehrdad Norouzi

Is the problem solved? (mkyong _ 123456)
Please let me know.

deepak
Guest
deepak

use password in AppConfig
ds.setUsername(“root”);
ds.setPassword(“root”);

ronny
Guest
ronny

can you pls give me database script ???

Sandeep
Guest
Sandeep

Hi MkYong, Thanks for great article. One question as why did yo first created set of setAuths and then converted to list.can we use list in first place and then return as list?

chris
Guest
chris

A set ensures unique items, so that way no auth would be added twice, and then it’s converted to a list since lists are easier to manipulate than sets

Sreejith
Guest
Sreejith

set you DB password in AppConfig.java

ds.setUsername(“root”);

ds.setPassword(“********”);

return ds;

Tan Nguyen
Guest
Tan Nguyen

Then I added a password but it still does not work, even though I have entered the correct username and password, it always returns “Invalid username and password!”

Kurniawan
Guest
Kurniawan

good tutorial!
but how to add user with role ?

Sourav Ken
Guest
Sourav Ken

Try to check your pojo. There catalog my have different schema then what ever schema you are using. Same issue I face when my db name and what is present under catalog in pojo is not same.

Tushar
Guest
Tushar

Where is login page verifies the username entered in username and password with the database? Please guide me, I am new to spring MVC wiring!

Basha G
Guest
Basha G

Help me to include the css files

Giancarlo Ventura Granados
Guest
Giancarlo Ventura Granados

I want to know it, too

Deivid NN
Guest
Deivid NN

its possible integrate this tutorial into project jsf 2?

Dheeraj Kumar
Guest
Dheeraj Kumar

very nice tutorial.thanks for sharing…