Spring Security + Hibernate Annotation Example
In this tutorial, previous Spring Security + Hibernate4 XML example will be reused, and convert it to a annotation-based example.
Technologies used :
- Spring 3.2.8.RELEASE
- Spring Security 3.2.3.RELEASE
- Hibernate 4.2.11.Final
- MySQL Server 5.6
- Tomcat 7 (Servlet 3.x container)
Quick Note :
- Create a session factory with
LocalSessionFactoryBuilder
- Inject session factory into a UserDao
- Integrate UserDao into a custom
UserDetailsService
, to load users from the database.
1. Project Directory
A final project directory structure.
2. User Model + Mapping File
Model classes and its’ annotation-based mapping file.
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;
}
}
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.
package com.mkyong.users.dao;
import com.mkyong.users.model.User;
public interface UserDao {
User findByUserName(String username);
}
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.
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.
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.
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.
6.2 Enter user “mkyong” and password “123456”.
6.3 Try access /admin
page with user “alex” and password “123456”, a 403 page will be displayed.
Download Source Code
References
- Spring Security + Hibernate XML Example
- Spring Security Hello World Annotation Example
- LocalSessionFactoryBuilder JavaDoc
- Spring ORM – Hibernate
- Spring Hibernate4 LocalSessionFactoryBean JavaDoc
- Spring Transaction Management
- Hibernate ORM documentation
- Spring Security Form Login Using Database, with JDBC
- Hibernate : No Session Found For Current Thread
but someone has solved the problem of the user name and password always incorrect ?
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
how did u get rid of this ?
It was the only example that worked for me. Thanks a lot.
damn. code is not working
no explanation no use
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
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) { .. }
Hello , can someone please explain how password is getting verified here ? As I see the db query is getting the user with only username.
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?
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
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
please share log or exception
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?
can’t even build it with maven
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
thanks, idiom)): return (User( sessionFactory.getCurrentSession()
.createQuery(“from User where username=?”)
.setParameter(0, username)
.list().get(0);
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);
very nice tutorial..thank you 🙂
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/
share jsp page
hi,
what is the salt. How is the password generated? I want to add registration feature
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((
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.
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);
tnx
I am getting the same error i.e: always displays ‘Invalid username and password’. Please some body help me out
just remove passwordEncoder or put $2a$10$NZ0s/78owi0SSVkNdDcEQ.EPW4u5r.HDSqjXZbOOzYUBc1JrzGbHC to your database passwordfield and use 123456 on web password field.
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?
Resolved! I forgot to set the Role, thanks
i didnt get any solution
pls give me the solution i also checked db conections and catalog in db
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.
Is the problem solved? (mkyong _ 123456)
Please let me know.
use password in AppConfig
ds.setUsername(“root”);
ds.setPassword(“root”);
can you pls give me database script ???
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?
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
set you DB password in AppConfig.java
ds.setUsername(“root”);
ds.setPassword(“********”);
return ds;
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!”
good tutorial!
but how to add user with role ?
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.
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!