Alexey Zvolinskiy

About 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.

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.
Related Whitepaper:

Introduction to Web Applications Development

Kick start your web apps development with this introductory ebook!

This 376 page eBook 'Introduction to Web Applications Development', starts with an introduction to the internet, including a brief history of the TCT/IP protocol and World Wide Web. It defines the basic concepts for web servers and studies the case of Apache, the most used webserver, while other free software webservers are not forgotten. It continues with webpage design focusing on HTML and JavaScript. XML Schemas, their validation and transformation are covered as well as dynamic webpages built with CGI, PHP or JSP and database access.

Get it Now!  

Leave a Reply


2 + five =



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close