Enterprise Java

Spring MVC: Security with MySQL and Hibernate

Spring has a lot of different modules. All of them are useful for the concrete purposes. Today I’m going to talk about Spring Security. This module provides flexible approach to manage permitions for access to different parts of web-application. In the post I’ll examine integration of Spring MVC, Hibernate, MySQL with Spring Security.

A regular case for any web-application is separation of functionality between some user groups. E.g. user with a “moderator” role can edit existing records in a database. An user
 
with “admin” role can do the same thing as the user with “moderator” role plus create new records. In Spring MVC application permition management can be implemented with the Spring Security.

The goal

As an example I will use sample Spring MVC application with Hibernate. The users and their roles will be stored in a database. MySQL will be used as the database. I’m going to create three tables: users, roles, user_roles. As you might guess the user_roles table is an intermediary table. In the application will be two roles: moderator and admin. There will be several pages with access for the moderator and for the admin.

Preparation

In order to make Spring Security available in a project, just add following dependencies in a pom.xml file:

<!-- Spring Security -->
		<dependency>
			<groupid>org.springframework.security</groupid>
			<artifactid>spring-security-core</artifactid>
			<version>3.1.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupid>org.springframework.security</groupid>
			<artifactid>spring-security-web</artifactid>
			<version>3.1.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupid>org.springframework.security</groupid>
			<artifactid>spring-security-config</artifactid>
			<version>3.1.3.RELEASE</version>
		</dependency>

I have to create three tables in the database and insert several records there.

CREATE TABLE `roles` (
  `id` int(6) NOT NULL AUTO_INCREMENT,
  `role` varchar(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

CREATE TABLE `users` (
  `id` int(6) NOT NULL AUTO_INCREMENT,
  `login` varchar(20) NOT NULL,
  `password` varchar(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

CREATE TABLE `user_roles` (
  `user_id` int(6) NOT NULL,
  `role_id` int(6) NOT NULL,
  KEY `user` (`user_id`),
  KEY `role` (`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

And here is a code for the roles and users:

INSERT INTO hibnatedb.roles (role) VALUES ('admin'), ('moderator');

INSERT INTO hibnatedb.users (login, password) VALUES ('moder', '111111'), ('adm', '222222');

INSERT INTO hibnatedb.user_roles (user_id, role_id) VALUES (1, 2), (2, 1);

Main part

The complete structure of project has the following structure:

Since you can find this project on GitHub, I’ll omit some things which are out of the current theme. I want to start from the heart of every web-project, I mean web.xml file. Spring Security is based on simple filters, so I need to add declaration of the filter in the deployment descriptor:

...
	<filter>
		<filter-name>springSecurityFilterChain</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>springSecurityFilterChain</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
...

Now it’s time to create entities for the users and roles tables:

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

	@Id
	@GeneratedValue
	private Integer id;

	private String login;

	private String password;

	@OneToOne(cascade=CascadeType.ALL)
	@JoinTable(name="user_roles",
		joinColumns = {@JoinColumn(name="user_id", referencedColumnName="id")},
		inverseJoinColumns = {@JoinColumn(name="role_id", referencedColumnName="id")}
	)
	private Role role;

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getLogin() {
		return login;
	}

	public void setLogin(String login) {
		this.login = login;
	}

	public String getPassword() {
		return password;
	}

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

	public Role getRole() {
		return role;
	}

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

}

And

@Entity
@Table(name="roles")
public class Role {

	@Id
	@GeneratedValue
	private Integer id;

	private String role;

	@OneToMany(cascade=CascadeType.ALL)
	@JoinTable(name="user_roles", 
		joinColumns = {@JoinColumn(name="role_id", referencedColumnName="id")},
		inverseJoinColumns = {@JoinColumn(name="user_id", referencedColumnName="id")}
	)
	private Set userRoles;

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getRole() {
		return role;
	}

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

	public Set getUserRoles() {
		return userRoles;
	}

	public void setUserRoles(Set userRoles) {
		this.userRoles = userRoles;
	}

}

Each entity class requires DAO and Service layer.

public interface UserDAO {

	public User getUser(String login);

}

And

@Repository
public class UserDAOImpl implements UserDAO {

	@Autowired
	private SessionFactory sessionFactory;

	private Session openSession() {
		return sessionFactory.getCurrentSession();
	}

	public User getUser(String login) {
		List userList = new ArrayList();
		Query query = openSession().createQuery("from User u where u.login = :login");
		query.setParameter("login", login);
		userList = query.list();
		if (userList.size() > 0)
			return userList.get(0);
		else
			return null;	
	}

}

Respectively for the Role class:

public interface RoleDAO {

	public Role getRole(int id);

}

And

@Repository
public class RoleDAOImpl implements RoleDAO {

	@Autowired
	private SessionFactory sessionFactory;

	private Session getCurrentSession() {
		return sessionFactory.getCurrentSession();
	}

	public Role getRole(int id) {
		Role role = (Role) getCurrentSession().load(Role.class, id);
		return role;
	}

}

The same pairs for the service layer:

public interface UserService {

	public User getUser(String login);

}

And

@Service
@Transactional
public class UserServiceImpl implements UserService {

	@Autowired
	private UserDAO userDAO;

	public User getUser(String login) {
		return userDAO.getUser(login);
	}

}

Respectively for the Role class:

public interface RoleService {

	public Role getRole(int id);

}

And

@Service
@Transactional
public class RoleServiceImpl implements RoleService {

	@Autowired
	private RoleDAO roleDAO;

	public Role getRole(int id) {
		return roleDAO.getRole(id);
	}

}

Everything above was just mechanical, routine code. Now let’s work on the Spring Security code. In order to plug in Spring Security into the project I have to create CustomUserDetailsService class and implement UserDetailsService interface.

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

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.sprsec.dao.UserDAO;

@Service
@Transactional(readOnly=true)
public class CustomUserDetailsService implements UserDetailsService {

	@Autowired
	private UserDAO userDAO;	

	public UserDetails loadUserByUsername(String login)
			throws UsernameNotFoundException {

		com.sprsec.model.User domainUser = userDAO.getUser(login);

		boolean enabled = true;
		boolean accountNonExpired = true;
		boolean credentialsNonExpired = true;
		boolean accountNonLocked = true;

		return new User(
				domainUser.getLogin(), 
				domainUser.getPassword(), 
				enabled, 
				accountNonExpired, 
				credentialsNonExpired, 
				accountNonLocked,
				getAuthorities(domainUser.getRole().getId())
		);
	}

	public Collection getAuthorities(Integer role) {
		List authList = getGrantedAuthorities(getRoles(role));
		return authList;
	}

	public List getRoles(Integer role) {

		List roles = new ArrayList();

		if (role.intValue() == 1) {
			roles.add("ROLE_MODERATOR");
			roles.add("ROLE_ADMIN");
		} else if (role.intValue() == 2) {
			roles.add("ROLE_MODERATOR");
		}
		return roles;
	}

	public static List getGrantedAuthorities(List roles) {
		List authorities = new ArrayList();

		for (String role : roles) {
			authorities.add(new SimpleGrantedAuthority(role));
		}
		return authorities;
	}

}

The main purpose of the class is to map User class of the application to the User class of Spring Security. This is one of the killer-feature of the Spring Security. In this way you can adapt any kind of Spring MVC application to usage of the Security module.

Controllers and Views

One of the most frequent question regarding Spring Security is how to create a custom login form. The answer is simple enough. You need to create a JSP file with a the form, and specify there action attribute ().

The most part of the URL-mapping depends on spring-security.xml file:

...
	<http auto-config="true">
	
		<intercept-url pattern="/sec/moderation.html" access="ROLE_MODERATOR">
		<intercept-url pattern="/admin/*" access="ROLE_ADMIN">
		
		<form-login login-page="/user-login.html" default-target-url="/success-login.html" authentication-failure-url="/error-login.html">
		<logout logout-success-url="/index.html">
		
	</logout></form-login></intercept-url></intercept-url></http>
	
	<authentication-manager>
		<authentication-provider user-service-ref="customUserDetailsService">
			<password-encoder hash="plaintext">
		</password-encoder></authentication-provider>
	</authentication-manager>
...

As you can see, I specified URLs for the: login page, default page after success login, error page for the situations when credentials are invalid. Also I declared URLs which require some access permitions. And the most important thing is a declaration of the authentication-manager. Through this Spring Security will use database to identify users and their roles.

Controllers:

@Controller
public class LinkNavigation {

	@RequestMapping(value="/", method=RequestMethod.GET)
	public ModelAndView homePage() {
		return new ModelAndView("home");
	}

	@RequestMapping(value="/index", method=RequestMethod.GET)
	public ModelAndView indexPage() {
		return new ModelAndView("home");
	}

	@RequestMapping(value="/sec/moderation", method=RequestMethod.GET)
	public ModelAndView moderatorPage() {
		return new ModelAndView("moderation");
	}

	@RequestMapping(value="/admin/first", method=RequestMethod.GET)
	public ModelAndView firstAdminPage() {
		return new ModelAndView("admin-first");
	}

	@RequestMapping(value="/admin/second", method=RequestMethod.GET)
	public ModelAndView secondAdminPage() {
		return new ModelAndView("admin-second");
	}

}

And

@Controller
public class SecurityNavigation {

	@RequestMapping(value="/user-login", method=RequestMethod.GET)
	public ModelAndView loginForm() {
		return new ModelAndView("login-form");
	}

	@RequestMapping(value="/error-login", method=RequestMethod.GET)
	public ModelAndView invalidLogin() {
		ModelAndView modelAndView = new ModelAndView("login-form");
		modelAndView.addObject("error", true);
		return modelAndView;
	}

	@RequestMapping(value="/success-login", method=RequestMethod.GET)
	public ModelAndView successLogin() {
		return new ModelAndView("success-login");
	}

}

Views you can see on GitHub.

Pay you attention to adding of @ImportResource(“classpath:spring-security.xml”) in the WebAppConfig java class.

Summary

I think this article will help you to dive into Spring Security. I used here Hibernate and MySQL since such combination of technologies isn’t used often in other tutorials in the internet. Probably you noticed that I used some XMLs in the project, that’s because currently there is no ways to implement all these stuff using annotation based approach.
 

Reference: Spring MVC: Security with MySQL and Hibernate from our JCG partner Alexey Zvolinskiy at the Fruzenshtein’s notes blog.

Alexey Zvolinskiy

Alexey is a test developer with solid experience in automation of web-applications using Java, TestNG and Selenium. He is so much into QA that even after work he provides training courses for junior QA engineers.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

6 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Petr Fiala
Petr Fiala
9 years ago

Hi,

nice example. I was going through your project from git and would like to ask why there is twice componentscan : 1. in RootConfig and 2. in WebAppConfig.

thank you

Petr F.

Mateusz
Mateusz
9 years ago

When i am trying to launch this project i get FAIL – Deployed application at context path /security-spr but context failed to start.
I do not know what is wrong.
Please help

harsh
harsh
8 years ago

Hi , How can i found this project on the git.

ray
ray
8 years ago

Can you post the web.xml so we can run the app?

ucup
8 years ago

thanks bro

mahadev
mahadev
7 years ago

hi…please mail me this application to my mai id mahadev.hallikhed130@gmail.com

Back to top button