Categories: springmvc

Spring MVC 4 + Spring Security 4 + Hibernate Example

In this post, we will build a full-blown Spring MVC application secured using Spring Security, integrating with MySQL database using Hibernate, handling Many-to-Many relationship on view, storing passwords in encrypted format using BCrypt, and providing RememberMe functionality using custom PersistentTokenRepository implementation with Hibernate HibernateTokenRepositoryImpl, retrieving the records from database and updating or deleting them within transaction, all using annotation configuration. Let’s get going.

This project can be served as a template for your own Spring MVC projects integrating Spring Security.

Note:

This post demonstrates a complete application with complete code. In order to manage the size of the post, i have skipped the textual descriptions of some basic stuff. In case you are interested in those details, this ,this & this post will help you.

Summary:

The project shows a simple user-management application. One can create a new user, edit or delete an existing user, and list all the users. User can be associated with one or more UserProfile, showing many-to-many relationship. URL’s of the applications are secured using Spring Security. That means, based on the roles of logged in user, access to certain URL’s will be granted or prohibited. On the view layer, user will see only the content he/she is allowed to based on the roles assigned to him/her, thanks to Spring Security tags for view layer.


Following technologies being used:

  • Spring 4.2.5.RELEASE
  • Spring Security 4.0.4.RELEASE
  • Hibernate Core 4.3.11.Final
  • validation-api 1.1.0.Final
  • hibernate-validator 5.1.3.Final
  • MySQL Server 5.6
  • Maven 3
  • JDK 1.7
  • Tomcat 8.0.21
  • Eclipse MARS.1 Release 4.5.1
  • logback 1.1.7

Let’s begin.

Step 1: Create the directory structure

Following will be the final project structure:

Let’s now add the content mentioned in above structure explaining each in detail.

Step 2: Update pom.xml to include required dependencies


<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
 xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

 <modelVersion>4.0.0</modelVersion>
 <groupId>com.websystique.springmvc</groupId>
 <artifactId>SpringMVCHibernateManyToManyCRUDExample</artifactId>
 <packaging>war</packaging>
 <version>1.0.0</version>
 <name>SpringMVCHibernateWithSpringSecurityExample</name>

   <properties>
  <springframework.version>4.2.5.RELEASE</springframework.version>
  <springsecurity.version>4.0.4.RELEASE</springsecurity.version>
  <hibernate.version>4.3.11.Final</hibernate.version>
  <mysql.connector.version>5.1.31</mysql.connector.version>
 </properties>

 <dependencies>
  <!-- Spring -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-core</artifactId>
   <version>${springframework.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-web</artifactId>
   <version>${springframework.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>${springframework.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-tx</artifactId>
   <version>${springframework.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-orm</artifactId>
   <version>${springframework.version}</version>
  </dependency>

  <!-- Spring Security -->
  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-web</artifactId>
   <version>${springsecurity.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-config</artifactId>
   <version>${springsecurity.version}</version>
  </dependency>
  <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-taglibs</artifactId>
      <version>${springsecurity.version}</version>
  </dependency>


  <!-- Hibernate -->
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-core</artifactId>
   <version>${hibernate.version}</version>
  </dependency>

  <!-- jsr303 validation -->
  <dependency>
   <groupId>javax.validation</groupId>
   <artifactId>validation-api</artifactId>
   <version>1.1.0.Final</version>
  </dependency>
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-validator</artifactId>
   <version>5.1.3.Final</version>
  </dependency>
  
  <!-- MySQL -->
  <dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>${mysql.connector.version}</version>
  </dependency>
  
  <!-- SLF4J/Logback -->
  <dependency>
   <groupId>ch.qos.logback</groupId>
   <artifactId>logback-classic</artifactId>
   <version>1.1.7</version>
  </dependency>

  <!-- Servlet+JSP+JSTL -->
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>javax.servlet-api</artifactId>
   <version>3.1.0</version>
  </dependency>
  <dependency>
   <groupId>javax.servlet.jsp</groupId>
   <artifactId>javax.servlet.jsp-api</artifactId>
   <version>2.3.1</version>
  </dependency>
  <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
  </dependency>
  
 </dependencies>

 <build>
  <pluginManagement>
   <plugins>
    <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-compiler-plugin</artifactId>
           <version>3.2</version>
           <configuration>
               <source>1.7</source>
               <target>1.7</target>
           </configuration>
       </plugin>
    <plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-war-plugin</artifactId>
     <version>2.4</version>
     <configuration>
      <warSourceDirectory>src/main/webapp</warSourceDirectory>
      <warName>SpringMVCHibernateWithSpringSecurityExample</warName>
      <failOnMissingWebXml>false</failOnMissingWebXml>
     </configuration>
    </plugin>
   </plugins>
  </pluginManagement>
  <finalName>SpringMVCHibernateWithSpringSecurityExample</finalName>
 </build>
</project>

Step 3: Configure Security

The first and foremost step to add spring security in our application is to create Spring Security Java Configuration. This configuration creates a Servlet Filter known as the springSecurityFilterChain which is responsible for all the security (protecting the application URLs, validating submitted username and passwords, redirecting to the log in form, etc) within our application

package com.websystique.springmvc.security;

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.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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;
import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

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

 @Autowired
 PersistentTokenRepository tokenRepository;

 @Autowired
 public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
  auth.userDetailsService(userDetailsService);
  auth.authenticationProvider(authenticationProvider());
 }

 @Override
 protected void configure(HttpSecurity http) throws Exception {
  http.authorizeRequests().antMatchers("/", "/list")
    .access("hasRole('USER') or hasRole('ADMIN') or hasRole('DBA')")
    .antMatchers("/newuser/**", "/delete-user-*").access("hasRole('ADMIN')").antMatchers("/edit-user-*")
    .access("hasRole('ADMIN') or hasRole('DBA')").and().formLogin().loginPage("/login")
    .loginProcessingUrl("/login").usernameParameter("ssoId").passwordParameter("password").and()
    .rememberMe().rememberMeParameter("remember-me").tokenRepository(tokenRepository)
    .tokenValiditySeconds(86400).and().csrf().and().exceptionHandling().accessDeniedPage("/Access_Denied");
 }

 @Bean
 public PasswordEncoder passwordEncoder() {
  return new BCryptPasswordEncoder();
 }

 @Bean
 public DaoAuthenticationProvider authenticationProvider() {
  DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
  authenticationProvider.setUserDetailsService(userDetailsService);
  authenticationProvider.setPasswordEncoder(passwordEncoder());
  return authenticationProvider;
 }

 @Bean
 public PersistentTokenBasedRememberMeServices getPersistentTokenBasedRememberMeServices() {
  PersistentTokenBasedRememberMeServices tokenBasedservice = new PersistentTokenBasedRememberMeServices(
    "remember-me", userDetailsService, tokenRepository);
  return tokenBasedservice;
 }

 @Bean
 public AuthenticationTrustResolver getAuthenticationTrustResolver() {
  return new AuthenticationTrustResolverImpl();
 }

}

As shown above, the access to URLs is governed as follows:

  • ‘/’ & ‘/list’ : Accessible to everyone
  • ‘/newuser’ & ‘/delete-user-*’ : Accessible only to Admin
  • ‘/edit-user-*’ : Accessible to Admin & DBA

Since we are storing the credentials in database, configuring DaoAuthenticationProvider with UserDetailsService would come handy. Additionally, in order to encrypt the password in database, we have chosen BCryptPasswordEncoder. Moreover, since we will also provide RememberMe functionality, keeping track of token-data in database, we configured a PersistentTokenRepository implementation.

Spring Security comes with two implementation of PersistentTokenRepository : JdbcTokenRepositoryImpl and InMemoryTokenRepositoryImpl. We could have opted for JdbcTokenRepositoryImpl [this post demonstrates the RememberMe with JdbcTokenRepositoryImpl], but since we are using Hibernate in our application, why not create a custom implementation using Hibernate instead of using JDBC? Shown below is an attempt for the same.

package com.websystique.springmvc.dao;

import java.util.Date;

import org.hibernate.Criteria;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.websystique.springmvc.dao.AbstractDao;
import com.websystique.springmvc.model.PersistentLogin;

@Repository("tokenRepositoryDao")
@Transactional
public class HibernateTokenRepositoryImpl extends AbstractDao<String, PersistentLogin>
  implements PersistentTokenRepository {

 static final Logger logger = LoggerFactory.getLogger(HibernateTokenRepositoryImpl.class);

 @Override
 public void createNewToken(PersistentRememberMeToken token) {
  logger.info("Creating Token for user : {}", token.getUsername());
  PersistentLogin persistentLogin = new PersistentLogin();
  persistentLogin.setUsername(token.getUsername());
  persistentLogin.setSeries(token.getSeries());
  persistentLogin.setToken(token.getTokenValue());
  persistentLogin.setLast_used(token.getDate());
  persist(persistentLogin);

 }

 @Override
 public PersistentRememberMeToken getTokenForSeries(String seriesId) {
  logger.info("Fetch Token if any for seriesId : {}", seriesId);
  try {
   Criteria crit = createEntityCriteria();
   crit.add(Restrictions.eq("series", seriesId));
   PersistentLogin persistentLogin = (PersistentLogin) crit.uniqueResult();

   return new PersistentRememberMeToken(persistentLogin.getUsername(), persistentLogin.getSeries(),
     persistentLogin.getToken(), persistentLogin.getLast_used());
  } catch (Exception e) {
   logger.info("Token not found...");
   return null;
  }
 }

 @Override
 public void removeUserTokens(String username) {
  logger.info("Removing Token if any for user : {}", username);
  Criteria crit = createEntityCriteria();
  crit.add(Restrictions.eq("username", username));
  PersistentLogin persistentLogin = (PersistentLogin) crit.uniqueResult();
  if (persistentLogin != null) {
   logger.info("rememberMe was selected");
   delete(persistentLogin);
  }

 }

 @Override
 public void updateToken(String seriesId, String tokenValue, Date lastUsed) {
  logger.info("Updating Token for seriesId : {}", seriesId);
  PersistentLogin persistentLogin = getByKey(seriesId);
  persistentLogin.setToken(tokenValue);
  persistentLogin.setLast_used(lastUsed);
  update(persistentLogin);
 }

}

Above implementation uses an Entity [PersistentLogin] mapped to persistent_logins table, shown below is the entity itself.

package com.websystique.springmvc.model;

import java.io.Serializable;
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
@Table(name="PERSISTENT_LOGINS")
public class PersistentLogin implements Serializable{

 @Id
 private String series;

 @Column(name="USERNAME", unique=true, nullable=false)
 private String username;
 
 @Column(name="TOKEN", unique=true, nullable=false)
 private String token;
 
 @Temporal(TemporalType.TIMESTAMP)
 private Date last_used;

 public String getSeries() {
  return series;
 }

 public void setSeries(String series) {
  this.series = series;
 }

 public String getUsername() {
  return username;
 }

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

 public String getToken() {
  return token;
 }

 public void setToken(String token) {
  this.token = token;
 }

 public Date getLast_used() {
  return last_used;
 }

 public void setLast_used(Date last_used) {
  this.last_used = last_used;
 }
 
 
}

The UserDetailsService implementation, used in Security configuration is shown below:

package com.websystique.springmvc.security;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.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.websystique.springmvc.model.User;
import com.websystique.springmvc.model.UserProfile;
import com.websystique.springmvc.service.UserService;


@Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService{

 static final Logger logger = LoggerFactory.getLogger(CustomUserDetailsService.class);
 
 @Autowired
 private UserService userService;
 
 @Transactional(readOnly=true)
 public UserDetails loadUserByUsername(String ssoId)
   throws UsernameNotFoundException {
  User user = userService.findBySSO(ssoId);
  logger.info("User : {}", user);
  if(user==null){
   logger.info("User not found");
   throw new UsernameNotFoundException("Username not found");
  }
   return new org.springframework.security.core.userdetails.User(user.getSsoId(), user.getPassword(), 
     true, true, true, true, getGrantedAuthorities(user));
 }

 
 private List<GrantedAuthority> getGrantedAuthorities(User user){
  List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
  
  for(UserProfile userProfile : user.getUserProfiles()){
   logger.info("UserProfile : {}", userProfile);
   authorities.add(new SimpleGrantedAuthority("ROLE_"+userProfile.getType()));
  }
  logger.info("authorities : {}", authorities);
  return authorities;
 }
 
}

Finally, register the springSecurityFilter with application war using below mentioned initializer class.

package com.websystique.springmvc.security;

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {

}

That’s all with Spring Security Configuration. Now let’s begin with Spring MVC part, discussing Hibernate configuration, necessary DAO, models & services along the way.

Step 4: Configure Hibernate

package com.websystique.springmvc.configuration;

import java.util.Properties;

import javax.sql.DataSource;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate4.HibernateTransactionManager;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
@ComponentScan({ "com.websystique.springmvc.configuration" })
@PropertySource(value = { "classpath:application.properties" })
public class HibernateConfiguration {

    @Autowired
    private Environment environment;

    @Bean
    public LocalSessionFactoryBean sessionFactory() {
        LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
        sessionFactory.setDataSource(dataSource());
        sessionFactory.setPackagesToScan(new String[] { "com.websystique.springmvc.model" });
        sessionFactory.setHibernateProperties(hibernateProperties());
        return sessionFactory;
     }
 
    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(environment.getRequiredProperty("jdbc.driverClassName"));
        dataSource.setUrl(environment.getRequiredProperty("jdbc.url"));
        dataSource.setUsername(environment.getRequiredProperty("jdbc.username"));
        dataSource.setPassword(environment.getRequiredProperty("jdbc.password"));
        return dataSource;
    }
    
    private Properties hibernateProperties() {
        Properties properties = new Properties();
        properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
        properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
        properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));
        return properties;        
    }
    
 @Bean
    @Autowired
    public HibernateTransactionManager transactionManager(SessionFactory s) {
       HibernateTransactionManager txManager = new HibernateTransactionManager();
       txManager.setSessionFactory(s);
       return txManager;
    }
}

Below is the properties file used in this post.
/src/main/resources/application.properties

jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/websystique
jdbc.username = myuser
jdbc.password = mypassword
hibernate.dialect = org.hibernate.dialect.MySQLDialect
hibernate.show_sql = true
hibernate.format_sql = true

Step 5: Configure Spring MVC

package com.websystique.springmvc.configuration;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

import com.websystique.springmvc.converter.RoleToUserProfileConverter;


@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.websystique.springmvc")
public class AppConfig extends WebMvcConfigurerAdapter{
 
 
 @Autowired
 RoleToUserProfileConverter roleToUserProfileConverter;
 

 /**
     * Configure ViewResolvers to deliver preferred views.
     */ @Override
 public void configureViewResolvers(ViewResolverRegistry registry) {

  InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
  viewResolver.setViewClass(JstlView.class);
  viewResolver.setPrefix("/WEB-INF/views/");
  viewResolver.setSuffix(".jsp");
  registry.viewResolver(viewResolver);
 }
 
 /**
     * Configure ResourceHandlers to serve static resources like CSS/ Javascript etc...
     */    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**").addResourceLocations("/static/");
    }
    
    /**
     * Configure Converter to be used.
     * In our example, we need a converter to convert string values[Roles] to UserProfiles in newUser.jsp
     */    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(roleToUserProfileConverter);
    }
 

    /**
     * Configure MessageSource to lookup any validation/error message in internationalized property files
     */    @Bean
 public MessageSource messageSource() {
     ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
     messageSource.setBasename("messages");
     return messageSource;
 }
    
    /**Optional. It's only required when handling '.' in @PathVariables which otherwise ignore everything after last '.' in @PathVaidables argument.
     * It's a known bug in Spring [https://jira.spring.io/browse/SPR-6164], still present in Spring 4.1.7.
     * This is a workaround for this issue.
     */    @Override
    public void configurePathMatch(PathMatchConfigurer matcher) {
        matcher.setUseRegisteredSuffixPatternMatch(true);
    }
}

The main highlight of this configuration is RoleToUserProfileConverter. It will take care of mapping the individual userProfile id’s on view to actual UserProfile Entities in database.

package com.websystique.springmvc.converter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

import com.websystique.springmvc.model.UserProfile;
import com.websystique.springmvc.service.UserProfileService;

/**
 * A converter class used in views to map id's to actual userProfile objects.
 */@Component
public class RoleToUserProfileConverter implements Converter<Object, UserProfile>{

 static final Logger logger = LoggerFactory.getLogger(RoleToUserProfileConverter.class);
 
 @Autowired
 UserProfileService userProfileService;

 /**
  * Gets UserProfile by Id
  * @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
  */ public UserProfile convert(Object element) {
  Integer id = Integer.parseInt((String)element);
  UserProfile profile= userProfileService.findById(id);
  logger.info("Profile : {}",profile);
  return profile;
 }
 
}

Since we are using JSR validators in our application to validate user input, we have configured the messages to be shown to user in case of validation failures. shown below is message.properties file:

NotEmpty.user.firstName=First name can not be blank.
NotEmpty.user.lastName=Last name can not be blank.
NotEmpty.user.email=Email can not be blank.
NotEmpty.user.password=Password can not be blank.
NotEmpty.user.ssoId=SSO ID can not be blank.
NotEmpty.user.userProfiles=At least one profile must be selected.
non.unique.ssoId=SSO ID {0} already exist. Please fill in different value.

Finally, the Spring Intializer class is shown below:

package com.websystique.springmvc.configuration;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

 @Override
 protected Class<?>[] getRootConfigClasses() {
  return new Class[] { AppConfig.class };
 }
 
 @Override
 protected Class<?>[] getServletConfigClasses() {
  return null;
 }
 
 @Override
 protected String[] getServletMappings() {
  return new String[] { "/" };
 }

}

Step 6: Create Spring Controller

package com.websystique.springmvc.controller;

import java.util.List;
import java.util.Locale;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.SessionAttributes;

import com.websystique.springmvc.model.User;
import com.websystique.springmvc.model.UserProfile;
import com.websystique.springmvc.service.UserProfileService;
import com.websystique.springmvc.service.UserService;



@Controller
@RequestMapping("/")
@SessionAttributes("roles")
public class AppController {

 @Autowired
 UserService userService;
 
 @Autowired
 UserProfileService userProfileService;
 
 @Autowired
 MessageSource messageSource;

 @Autowired
 PersistentTokenBasedRememberMeServices persistentTokenBasedRememberMeServices;
 
 @Autowired
 AuthenticationTrustResolver authenticationTrustResolver;
 
 
 /**
  * This method will list all existing users.
  */ @RequestMapping(value = { "/", "/list" }, method = RequestMethod.GET)
 public String listUsers(ModelMap model) {

  List<User> users = userService.findAllUsers();
  model.addAttribute("users", users);
  model.addAttribute("loggedinuser", getPrincipal());
  return "userslist";
 }

 /**
  * This method will provide the medium to add a new user.
  */ @RequestMapping(value = { "/newuser" }, method = RequestMethod.GET)
 public String newUser(ModelMap model) {
  User user = new User();
  model.addAttribute("user", user);
  model.addAttribute("edit", false);
  model.addAttribute("loggedinuser", getPrincipal());
  return "registration";
 }

 /**
  * This method will be called on form submission, handling POST request for
  * saving user in database. It also validates the user input
  */ @RequestMapping(value = { "/newuser" }, method = RequestMethod.POST)
 public String saveUser(@Valid User user, BindingResult result,
   ModelMap model) {

  if (result.hasErrors()) {
   return "registration";
  }

  /*
   * Preferred way to achieve uniqueness of field [sso] should be implementing custom @Unique annotation 
   * and applying it on field [sso] of Model class [User].
   * 
   * Below mentioned peace of code [if block] is to demonstrate that you can fill custom errors outside the validation
   * framework as well while still using internationalized messages.
   * 
   */  if(!userService.isUserSSOUnique(user.getId(), user.getSsoId())){
   FieldError ssoError =new FieldError("user","ssoId",messageSource.getMessage("non.unique.ssoId", new String[]{user.getSsoId()}, Locale.getDefault()));
      result.addError(ssoError);
   return "registration";
  }
  
  userService.saveUser(user);

  model.addAttribute("success", "User " + user.getFirstName() + " "+ user.getLastName() + " registered successfully");
  model.addAttribute("loggedinuser", getPrincipal());
  //return "success";
  return "registrationsuccess";
 }


 /**
  * This method will provide the medium to update an existing user.
  */ @RequestMapping(value = { "/edit-user-{ssoId}" }, method = RequestMethod.GET)
 public String editUser(@PathVariable String ssoId, ModelMap model) {
  User user = userService.findBySSO(ssoId);
  model.addAttribute("user", user);
  model.addAttribute("edit", true);
  model.addAttribute("loggedinuser", getPrincipal());
  return "registration";
 }
 
 /**
  * This method will be called on form submission, handling POST request for
  * updating user in database. It also validates the user input
  */ @RequestMapping(value = { "/edit-user-{ssoId}" }, method = RequestMethod.POST)
 public String updateUser(@Valid User user, BindingResult result,
   ModelMap model, @PathVariable String ssoId) {

  if (result.hasErrors()) {
   return "registration";
  }

  /*//Uncomment below 'if block' if you WANT TO ALLOW UPDATING SSO_ID in UI which is a unique key to a User.
  if(!userService.isUserSSOUnique(user.getId(), user.getSsoId())){
   FieldError ssoError =new FieldError("user","ssoId",messageSource.getMessage("non.unique.ssoId", new String[]{user.getSsoId()}, Locale.getDefault()));
      result.addError(ssoError);
   return "registration";
  }*/

  userService.updateUser(user);

  model.addAttribute("success", "User " + user.getFirstName() + " "+ user.getLastName() + " updated successfully");
  model.addAttribute("loggedinuser", getPrincipal());
  return "registrationsuccess";
 }

 
 /**
  * This method will delete an user by it's SSOID value.
  */ @RequestMapping(value = { "/delete-user-{ssoId}" }, method = RequestMethod.GET)
 public String deleteUser(@PathVariable String ssoId) {
  userService.deleteUserBySSO(ssoId);
  return "redirect:/list";
 }
 

 /**
  * This method will provide UserProfile list to views
  */ @ModelAttribute("roles")
 public List<UserProfile> initializeProfiles() {
  return userProfileService.findAll();
 }
 
 /**
  * This method handles Access-Denied redirect.
  */ @RequestMapping(value = "/Access_Denied", method = RequestMethod.GET)
 public String accessDeniedPage(ModelMap model) {
  model.addAttribute("loggedinuser", getPrincipal());
  return "accessDenied";
 }

 /**
  * This method handles login GET requests.
  * If users is already logged-in and tries to goto login page again, will be redirected to list page.
  */ @RequestMapping(value = "/login", method = RequestMethod.GET)
 public String loginPage() {
  if (isCurrentAuthenticationAnonymous()) {
   return "login";
     } else {
      return "redirect:/list";  
     }
 }

 /**
  * This method handles logout requests.
  * Toggle the handlers if you are RememberMe functionality is useless in your app.
  */ @RequestMapping(value="/logout", method = RequestMethod.GET)
 public String logoutPage (HttpServletRequest request, HttpServletResponse response){
  Authentication auth = SecurityContextHolder.getContext().getAuthentication();
  if (auth != null){    
   //new SecurityContextLogoutHandler().logout(request, response, auth);
   persistentTokenBasedRememberMeServices.logout(request, response, auth);
   SecurityContextHolder.getContext().setAuthentication(null);
  }
  return "redirect:/login?logout";
 }

 /**
  * This method returns the principal[user-name] of logged-in user.
  */ private String getPrincipal(){
  String userName = null;
  Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

  if (principal instanceof UserDetails) {
   userName = ((UserDetails)principal).getUsername();
  } else {
   userName = principal.toString();
  }
  return userName;
 }
 
 /**
  * This method returns true if users is already authenticated [logged-in], else false.
  */ private boolean isCurrentAuthenticationAnonymous() {
     final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
     return authenticationTrustResolver.isAnonymous(authentication);
 }


}

This is a trivial Spring MVC controller. Comments on Each method provide the explanations.

Step 7: Create Models

package com.websystique.springmvc.model;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

import org.hibernate.validator.constraints.NotEmpty;

@Entity
@Table(name="APP_USER")
public class User implements Serializable{

 @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
 private Integer id;

 @NotEmpty
 @Column(name="SSO_ID", unique=true, nullable=false)
 private String ssoId;
 
 @NotEmpty
 @Column(name="PASSWORD", nullable=false)
 private String password;
  
 @NotEmpty
 @Column(name="FIRST_NAME", nullable=false)
 private String firstName;

 @NotEmpty
 @Column(name="LAST_NAME", nullable=false)
 private String lastName;

 @NotEmpty
 @Column(name="EMAIL", nullable=false)
 private String email;

 @NotEmpty
 @ManyToMany(fetch = FetchType.LAZY)
 @JoinTable(name = "APP_USER_USER_PROFILE", 
             joinColumns = { @JoinColumn(name = "USER_ID") }, 
             inverseJoinColumns = { @JoinColumn(name = "USER_PROFILE_ID") })
 private Set<UserProfile> userProfiles = new HashSet<UserProfile>();

 public Integer getId() {
  return id;
 }

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

 public String getSsoId() {
  return ssoId;
 }

 public void setSsoId(String ssoId) {
  this.ssoId = ssoId;
 }

 public String getPassword() {
  return password;
 }

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

 public String getFirstName() {
  return firstName;
 }

 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }

 public String getLastName() {
  return lastName;
 }

 public void setLastName(String lastName) {
  this.lastName = lastName;
 }

 public String getEmail() {
  return email;
 }

 public void setEmail(String email) {
  this.email = email;
 }

 public Set<UserProfile> getUserProfiles() {
  return userProfiles;
 }

 public void setUserProfiles(Set<UserProfile> userProfiles) {
  this.userProfiles = userProfiles;
 }

 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + ((id == null) ? 0 : id.hashCode());
  result = prime * result + ((ssoId == null) ? 0 : ssoId.hashCode());
  return result;
 }

 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (!(obj instanceof User))
   return false;
  User other = (User) obj;
  if (id == null) {
   if (other.id != null)
    return false;
  } else if (!id.equals(other.id))
   return false;
  if (ssoId == null) {
   if (other.ssoId != null)
    return false;
  } else if (!ssoId.equals(other.ssoId))
   return false;
  return true;
 }

 /*
  * DO-NOT-INCLUDE passwords in toString function.
  * It is done here just for convenience purpose.
  */ @Override
 public String toString() {
  return "User [id=" + id + ", ssoId=" + ssoId + ", password=" + password
    + ", firstName=" + firstName + ", lastName=" + lastName
    + ", email=" + email + "]";
 }


 
}
package com.websystique.springmvc.model;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="USER_PROFILE")
public class UserProfile implements Serializable{

 @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
 private Integer id; 

 @Column(name="TYPE", length=15, unique=true, nullable=false)
 private String type = UserProfileType.USER.getUserProfileType();
 
 public Integer getId() {
  return id;
 }

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

 public String getType() {
  return type;
 }

 public void setType(String type) {
  this.type = type;
 }

 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + ((id == null) ? 0 : id.hashCode());
  result = prime * result + ((type == null) ? 0 : type.hashCode());
  return result;
 }

 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (!(obj instanceof UserProfile))
   return false;
  UserProfile other = (UserProfile) obj;
  if (id == null) {
   if (other.id != null)
    return false;
  } else if (!id.equals(other.id))
   return false;
  if (type == null) {
   if (other.type != null)
    return false;
  } else if (!type.equals(other.type))
   return false;
  return true;
 }

 @Override
 public String toString() {
  return "UserProfile [id=" + id + ", type=" + type + "]";
 }




}
package com.websystique.springmvc.model;

import java.io.Serializable;

public enum UserProfileType implements Serializable{
 USER("USER"),
 DBA("DBA"),
 ADMIN("ADMIN");
 
 String userProfileType;
 
 private UserProfileType(String userProfileType){
  this.userProfileType = userProfileType;
 }
 
 public String getUserProfileType(){
  return userProfileType;
 }
 
}

Step 7: Create DAOs

package com.websystique.springmvc.dao;

import java.io.Serializable;

import java.lang.reflect.ParameterizedType;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;

public abstract class AbstractDao<PK extends Serializable, T> {
 
 private final Class<T> persistentClass;
 
 @SuppressWarnings("unchecked")
 public AbstractDao(){
  this.persistentClass =(Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1];
 }
 
 @Autowired
 private SessionFactory sessionFactory;

 protected Session getSession(){
  return sessionFactory.getCurrentSession();
 }

 @SuppressWarnings("unchecked")
 public T getByKey(PK key) {
  return (T) getSession().get(persistentClass, key);
 }

 public void persist(T entity) {
  getSession().persist(entity);
 }

 public void update(T entity) {
  getSession().update(entity);
 }

 public void delete(T entity) {
  getSession().delete(entity);
 }
 
 protected Criteria createEntityCriteria(){
  return getSession().createCriteria(persistentClass);
 }

}
package com.websystique.springmvc.dao;

import java.util.List;

import com.websystique.springmvc.model.User;


public interface UserDao {

 User findById(int id);
 
 User findBySSO(String sso);
 
 void save(User user);
 
 void deleteBySSO(String sso);
 
 List<User> findAllUsers();

}
package com.websystique.springmvc.dao;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;

import com.websystique.springmvc.model.User;



@Repository("userDao")
public class UserDaoImpl extends AbstractDao<Integer, User> implements UserDao {

 static final Logger logger = LoggerFactory.getLogger(UserDaoImpl.class);
 
 public User findById(int id) {
  User user = getByKey(id);
  if(user!=null){
   Hibernate.initialize(user.getUserProfiles());
  }
  return user;
 }

 public User findBySSO(String sso) {
  logger.info("SSO : {}", sso);
  Criteria crit = createEntityCriteria();
  crit.add(Restrictions.eq("ssoId", sso));
  User user = (User)crit.uniqueResult();
  if(user!=null){
   Hibernate.initialize(user.getUserProfiles());
  }
  return user;
 }

 @SuppressWarnings("unchecked")
 public List<User> findAllUsers() {
  Criteria criteria = createEntityCriteria().addOrder(Order.asc("firstName"));
  criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);//To avoid duplicates.
  List<User> users = (List<User>) criteria.list();
  
  // No need to fetch userProfiles since we are not showing them on list page. Let them lazy load. 
  // Uncomment below lines for eagerly fetching of userProfiles if you want.
  /*
  for(User user : users){
   Hibernate.initialize(user.getUserProfiles());
  }*/  return users;
 }

 public void save(User user) {
  persist(user);
 }

 public void deleteBySSO(String sso) {
  Criteria crit = createEntityCriteria();
  crit.add(Restrictions.eq("ssoId", sso));
  User user = (User)crit.uniqueResult();
  delete(user);
 }

}
package com.websystique.springmvc.dao;

import java.util.List;

import com.websystique.springmvc.model.UserProfile;


public interface UserProfileDao {

 List<UserProfile> findAll();
 
 UserProfile findByType(String type);
 
 UserProfile findById(int id);
}
package com.websystique.springmvc.dao;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Repository;

import com.websystique.springmvc.model.UserProfile;



@Repository("userProfileDao")
public class UserProfileDaoImpl extends AbstractDao<Integer, UserProfile>implements UserProfileDao{

 public UserProfile findById(int id) {
  return getByKey(id);
 }

 public UserProfile findByType(String type) {
  Criteria crit = createEntityCriteria();
  crit.add(Restrictions.eq("type", type));
  return (UserProfile) crit.uniqueResult();
 }
 
 @SuppressWarnings("unchecked")
 public List<UserProfile> findAll(){
  Criteria crit = createEntityCriteria();
  crit.addOrder(Order.asc("type"));
  return (List<UserProfile>)crit.list();
 }
 
}

Step 8: Create Services

package com.websystique.springmvc.service;

import java.util.List;

import com.websystique.springmvc.model.User;


public interface UserService {
 
 User findById(int id);
 
 User findBySSO(String sso);
 
 void saveUser(User user);
 
 void updateUser(User user);
 
 void deleteUserBySSO(String sso);

 List<User> findAllUsers(); 
 
 boolean isUserSSOUnique(Integer id, String sso);

}
package com.websystique.springmvc.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.websystique.springmvc.dao.UserDao;
import com.websystique.springmvc.model.User;


@Service("userService")
@Transactional
public class UserServiceImpl implements UserService{

 @Autowired
 private UserDao dao;

 @Autowired
    private PasswordEncoder passwordEncoder;
 
 public User findById(int id) {
  return dao.findById(id);
 }

 public User findBySSO(String sso) {
  User user = dao.findBySSO(sso);
  return user;
 }

 public void saveUser(User user) {
  user.setPassword(passwordEncoder.encode(user.getPassword()));
  dao.save(user);
 }

 /*
  * Since the method is running with Transaction, No need to call hibernate update explicitly.
  * Just fetch the entity from db and update it with proper values within transaction.
  * It will be updated in db once transaction ends. 
  */ public void updateUser(User user) {
  User entity = dao.findById(user.getId());
  if(entity!=null){
   entity.setSsoId(user.getSsoId());
   if(!user.getPassword().equals(entity.getPassword())){
    entity.setPassword(passwordEncoder.encode(user.getPassword()));
   }
   entity.setFirstName(user.getFirstName());
   entity.setLastName(user.getLastName());
   entity.setEmail(user.getEmail());
   entity.setUserProfiles(user.getUserProfiles());
  }
 }

 
 public void deleteUserBySSO(String sso) {
  dao.deleteBySSO(sso);
 }

 public List<User> findAllUsers() {
  return dao.findAllUsers();
 }

 public boolean isUserSSOUnique(Integer id, String sso) {
  User user = findBySSO(sso);
  return ( user == null || ((id != null) && (user.getId() == id)));
 }
 
}
package com.websystique.springmvc.service;

import java.util.List;

import com.websystique.springmvc.model.UserProfile;


public interface UserProfileService {

 UserProfile findById(int id);

 UserProfile findByType(String type);
 
 List<UserProfile> findAll();
 
}
package com.websystique.springmvc.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.websystique.springmvc.dao.UserProfileDao;
import com.websystique.springmvc.model.UserProfile;


@Service("userProfileService")
@Transactional
public class UserProfileServiceImpl implements UserProfileService{
 
 @Autowired
 UserProfileDao dao;
 
 public UserProfile findById(int id) {
  return dao.findById(id);
 }

 public UserProfile findByType(String type){
  return dao.findByType(type);
 }

 public List<UserProfile> findAll() {
  return dao.findAll();
 }
}

Step 9: Create Views

Start with login page,asking username & password, and optionally ‘RememberMe’ flag.

WEB-INF/views/login.jsp

&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=ISO-8859-1&quot; pageEncoding=&quot;ISO-8859-1&quot;%&gt;
&lt;%@ page isELIgnored=&quot;false&quot; %&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot;%&gt;
&lt;html&gt;
 &lt;head&gt;
  &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=ISO-8859-1&quot;&gt;
  &lt;title&gt;Login page&lt;/title&gt;
  &lt;link href=&quot;&lt;c:url value='/static/css/bootstrap.css' /&gt;&quot;  rel=&quot;stylesheet&quot;&gt;&lt;/link&gt;
  &lt;link href=&quot;&lt;c:url value='/static/css/app.css' /&gt;&quot; rel=&quot;stylesheet&quot;&gt;&lt;/link&gt;
  &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.css&quot; /&gt;
 &lt;/head&gt;

 &lt;body&gt;
  &lt;div id=&quot;mainWrapper&quot;&gt;
   &lt;div class=&quot;login-container&quot;&gt;
    &lt;div class=&quot;login-card&quot;&gt;
     &lt;div class=&quot;login-form&quot;&gt;
      &lt;c:url var=&quot;loginUrl&quot; value=&quot;/login&quot; /&gt;
      &lt;form action=&quot;${loginUrl}&quot; method=&quot;post&quot; class=&quot;form-horizontal&quot;&gt;
       &lt;c:if test=&quot;${param.error != null}&quot;&gt;
        &lt;div class=&quot;alert alert-danger&quot;&gt;
         &lt;p&gt;Invalid username and password.&lt;/p&gt;
        &lt;/div&gt;
       &lt;/c:if&gt;
       &lt;c:if test=&quot;${param.logout != null}&quot;&gt;
        &lt;div class=&quot;alert alert-success&quot;&gt;
         &lt;p&gt;You have been logged out successfully.&lt;/p&gt;
        &lt;/div&gt;
       &lt;/c:if&gt;
       &lt;div class=&quot;input-group input-sm&quot;&gt;
        &lt;label class=&quot;input-group-addon&quot; >WEB-INF/views/userslist.jsp</code>

&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=ISO-8859-1&quot; pageEncoding=&quot;ISO-8859-1&quot;%&gt;
&lt;%@ page isELIgnored=&quot;false&quot; %&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;%@ taglib prefix=&quot;sec&quot; uri=&quot;http://www.springframework.org/security/tags&quot;%&gt;

&lt;html&gt;

&lt;head&gt;
 &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=ISO-8859-1&quot;&gt;
 &lt;title&gt;Users List&lt;/title&gt;
 &lt;link href=&quot;&lt;c:url value='/static/css/bootstrap.css' /&gt;&quot; rel=&quot;stylesheet&quot;&gt;&lt;/link&gt;
 &lt;link href=&quot;&lt;c:url value='/static/css/app.css' /&gt;&quot; rel=&quot;stylesheet&quot;&gt;&lt;/link&gt;
&lt;/head&gt;

&lt;body&gt;
 &lt;div class=&quot;generic-container&quot;&gt;
  &lt;%@include file=&quot;authheader.jsp&quot; %&gt; 
  &lt;div class=&quot;panel panel-default&quot;&gt;
     &lt;!-- Default panel contents --&gt;
     &lt;div class=&quot;panel-heading&quot;&gt;&lt;span class=&quot;lead&quot;&gt;List of Users &lt;/span&gt;&lt;/div&gt;
   &lt;table class=&quot;table table-hover&quot;&gt;
       &lt;thead&gt;
          &lt;tr&gt;
            &lt;th&gt;Firstname&lt;/th&gt;
            &lt;th&gt;Lastname&lt;/th&gt;
            &lt;th&gt;Email&lt;/th&gt;
            &lt;th&gt;SSO ID&lt;/th&gt;
            &lt;sec:authorize access=&quot;hasRole('ADMIN') or hasRole('DBA')&quot;&gt;
             &lt;th width=&quot;100&quot;&gt;&lt;/th&gt;
            &lt;/sec:authorize&gt;
            &lt;sec:authorize access=&quot;hasRole('ADMIN')&quot;&gt;
             &lt;th width=&quot;100&quot;&gt;&lt;/th&gt;
            &lt;/sec:authorize&gt;
            
     &lt;/tr&gt;
       &lt;/thead&gt;
       &lt;tbody&gt;
    &lt;c:forEach items=&quot;${users}&quot; var=&quot;user&quot;&gt;
     &lt;tr&gt;
      &lt;td&gt;${user.firstName}&lt;/td&gt;
      &lt;td&gt;${user.lastName}&lt;/td&gt;
      &lt;td&gt;${user.email}&lt;/td&gt;
      &lt;td&gt;${user.ssoId}&lt;/td&gt;
         &lt;sec:authorize access=&quot;hasRole('ADMIN') or hasRole('DBA')&quot;&gt;
       &lt;td&gt;&lt;a href=&quot;&lt;c:url value='/edit-user-${user.ssoId}' /&gt;&quot; class=&quot;btn btn-success custom-width&quot;&gt;edit&lt;/a&gt;&lt;/td&gt;
            &lt;/sec:authorize&gt;
            &lt;sec:authorize access=&quot;hasRole('ADMIN')&quot;&gt;
       &lt;td&gt;&lt;a href=&quot;&lt;c:url value='/delete-user-${user.ssoId}' /&gt;&quot; class=&quot;btn btn-danger custom-width&quot;&gt;delete&lt;/a&gt;&lt;/td&gt;
            &lt;/sec:authorize&gt;
     &lt;/tr&gt;
    &lt;/c:forEach&gt;
       &lt;/tbody&gt;
      &lt;/table&gt;
  &lt;/div&gt;
  &lt;sec:authorize access=&quot;hasRole('ADMIN')&quot;&gt;
    &lt;div class=&quot;well&quot;&gt;
     &lt;a href=&quot;&lt;c:url value='/newuser' /&gt;&quot;&gt;Add New User&lt;/a&gt;
    &lt;/div&gt;
   &lt;/sec:authorize&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;

Above page also includes a jsp containing welcome-messagealong with Logout link as shown below:

WEB-INF/views/authheader.jsp

 <div class="authbar">
  <span>Dear <strong>${loggedinuser}</strong>, Welcome to CrazyUsers.</span> <span class="floatRight"><a href="<c:url value="/logout" />">Logout</a></span>
 </div>

A user with ‘Admin’ role can add a new user. Shown below is the registration page for the same.
WEB-INF/views/registration.jsp

&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=ISO-8859-1&quot; pageEncoding=&quot;ISO-8859-1&quot;%&gt;
&lt;%@ page isELIgnored=&quot;false&quot; %&gt;
&lt;%@ taglib prefix=&quot;form&quot; uri=&quot;http://www.springframework.org/tags/form&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;

&lt;html&gt;

&lt;head&gt;
 &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=ISO-8859-1&quot;&gt;
 &lt;title&gt;User Registration Form&lt;/title&gt;
 &lt;link href=&quot;&lt;c:url value='/static/css/bootstrap.css' /&gt;&quot; rel=&quot;stylesheet&quot;&gt;&lt;/link&gt;
 &lt;link href=&quot;&lt;c:url value='/static/css/app.css' /&gt;&quot; rel=&quot;stylesheet&quot;&gt;&lt;/link&gt;
&lt;/head&gt;

&lt;body&gt;
  &lt;div class=&quot;generic-container&quot;&gt;
  &lt;%@include file=&quot;authheader.jsp&quot; %&gt;

  &lt;div class=&quot;well lead&quot;&gt;User Registration Form&lt;/div&gt;
   &lt;form:form method=&quot;POST&quot; modelAttribute=&quot;user&quot; class=&quot;form-horizontal&quot;&gt;
   &lt;form:input type=&quot;hidden&quot; path=&quot;id&quot; id=&quot;id&quot;/&gt;
   
   &lt;div class=&quot;row&quot;&gt;
    &lt;div class=&quot;form-group col-md-12&quot;&gt;
     &lt;label class=&quot;col-md-3 control-lable&quot; >WEB-INF/views/registrationsuccess.jsp</code>

&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=ISO-8859-1&quot; pageEncoding=&quot;ISO-8859-1&quot;%&gt;
&lt;%@ page isELIgnored=&quot;false&quot; %&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;


&lt;html&gt;
&lt;head&gt;
 &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=ISO-8859-1&quot;&gt;
 &lt;title&gt;Registration Confirmation Page&lt;/title&gt;
 &lt;link href=&quot;&lt;c:url value='/static/css/bootstrap.css' /&gt;&quot; rel=&quot;stylesheet&quot;&gt;&lt;/link&gt;
 &lt;link href=&quot;&lt;c:url value='/static/css/app.css' /&gt;&quot; rel=&quot;stylesheet&quot;&gt;&lt;/link&gt;
&lt;/head&gt;
&lt;body&gt;
 &lt;div class=&quot;generic-container&quot;&gt;
  &lt;%@include file=&quot;authheader.jsp&quot; %&gt;
  
  &lt;div class=&quot;alert alert-success lead&quot;&gt;
      ${success}
  &lt;/div&gt;
  
  &lt;span class=&quot;well floatRight&quot;&gt;
   Go to &lt;a href=&quot;&lt;c:url value='/list' /&gt;&quot;&gt;Users List&lt;/a&gt;
  &lt;/span&gt;
 &lt;/div&gt;
&lt;/body&gt;

&lt;/html&gt;

AccessDenied page will be shown if the users is not allowed to go to certain url’s.

WEB-INF/views/accessDenied.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ page isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
 <title>AccessDenied page</title>
</head>
<body>
 <div class="generic-container">
  <div class="authbar">
   <span>Dear <strong>${loggedinuser}</strong>, You are not authorized to access this page.</span> <span class="floatRight"><a href="<c:url value="/logout" />">Logout</a></span>
  </div>
 </div>
</body>
</html>

Step 10: Create and populate schema in database

/*All User's gets stored in APP_USER table*/create table APP_USER (
   id BIGINT NOT NULL AUTO_INCREMENT,
   sso_id VARCHAR(30) NOT NULL,
   password VARCHAR(100) NOT NULL,
   first_name VARCHAR(30) NOT NULL,
   last_name  VARCHAR(30) NOT NULL,
   email VARCHAR(30) NOT NULL,
   PRIMARY KEY (id),
   UNIQUE (sso_id)
);
  
/* USER_PROFILE table contains all possible roles */ 
create table USER_PROFILE(
   id BIGINT NOT NULL AUTO_INCREMENT,
   type VARCHAR(30) NOT NULL,
   PRIMARY KEY (id),
   UNIQUE (type)
);
  
/* JOIN TABLE for MANY-TO-MANY relationship*/  
CREATE TABLE APP_USER_USER_PROFILE (
    user_id BIGINT NOT NULL,
    user_profile_id BIGINT NOT NULL,
    PRIMARY KEY (user_id, user_profile_id),
    CONSTRAINT FK_APP_USER FOREIGN KEY (user_id) REFERENCES APP_USER (id),
    CONSTRAINT FK_USER_PROFILE FOREIGN KEY (user_profile_id) REFERENCES USER_PROFILE (id)
);
 
/* Populate USER_PROFILE Table */INSERT INTO USER_PROFILE(type)
VALUES ('USER');
 
INSERT INTO USER_PROFILE(type)
VALUES ('ADMIN');
 
INSERT INTO USER_PROFILE(type)
VALUES ('DBA');
 
 
/* Populate one Admin User which will further create other users for the application using GUI */INSERT INTO APP_USER(sso_id, password, first_name, last_name, email)
VALUES ('sam','$2a$10$4eqIF5s/ewJwHK1p8lqlFOEm2QIA0S8g6./Lok.pQxqcxaBZYChRm', 'Sam','Smith','samy@xyz.com');
 
 
/* Populate JOIN Table */INSERT INTO APP_USER_USER_PROFILE (user_id, user_profile_id)
  SELECT user.id, profile.id FROM app_user user, user_profile profile
  where user.sso_id='sam' and profile.type='ADMIN';

/* Create persistent_logins Table used to store rememberme related stuff*/CREATE TABLE persistent_logins (
    username VARCHAR(64) NOT NULL,
    series VARCHAR(64) NOT NULL,
    token VARCHAR(64) NOT NULL,
    last_used TIMESTAMP NOT NULL,
    PRIMARY KEY (series)
);

Note that we have inserted one user manually(we do need one Admin user to actually login and create further users for application). This is a real-world scenario. Notice the password which is encrypted form of password ‘abc125’. It’s generated using below mentioned utility class [it could even have been a script] which is used only and only to generate a password for one initial Admin user. It can well be removed from application.

package com.websystique.springsecurity.util;
 
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 
public class QuickPasswordEncodingGenerator {
 
    /**
     * @param args
     */    public static void main(String[] args) {
            String password = "abc125";
            BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
            System.out.println(passwordEncoder.encode(password));
    }
 
}

Step 11: Build, deploy and Run Application

Now build the war (either by eclipse as was mentioned in previous tutorials) or via maven command line( mvn clean install). Deploy the war to a Servlet 3.0 container . Since here i am using Tomcat, i will simply put this war file into tomcat webapps folder and click on start.bat inside tomcat/bin directory.

If you prefer to deploy from within Eclipse using tomcat: For those of us, who prefer to deploy and run from within eclipse, and might be facing difficulties setting Eclipse with tomcat, the detailed step-by-step solution can be found at : How to setup tomcat with Eclipse.

Open browser and browse at http://localhost:8080/SpringMVCHibernateWithSpringSecurityExample/

Login with User Sam & password abc125, check RememberMe as well.

Check database now.An entry should be made in persistent_logins table.

Nothing changes for APP_USER table though.

Now click on ‘Add new user’ link. Add a user with ‘USER’ role.

Click on Register, user should be added.

Click on ‘Users List’ link. You should see the newly added user.

Add another user with DBA & USER role.

Register. Now check the list again.

Verify APP_USER table.

Now logout.

Check persistent_logins table, entry should be removed.

Login with user ‘will’ which has ‘User’ role. No Add/Edit/Delete links are available to this user.

Now logout and login with ‘bob’. No Add/Delete links are available to this user.

Now try to manually type the delete URL in browser-bar and enter.You should see AccessDenied page.

That’s it. As we saw, it’s rather simple to integrate Spring Security with Spring MVC. Feel free to Comment, and suggest improvements.

Download Source Code

References

View Comments

  • Hello it s such a great tutorial but I would like to know how to implement a the authentication based with a dropdown list in order to select a role in case a user has multiple role

  • Hello websystique
    This is amazing website
    I have learned lot of things from this website. Firstly I want to say thanks for making this website.
    Sir I have one issue I am developing a website but I want to open home page firstly not login page. I update configure () method like
    http.authorizeRequest ().
    .antMatcher("/","/home").permitAll()
    .antMatcher("/list")
    .access ("hasRole ('USER') or hasRole ('ADMIN') or hasRole ('DBA')")
    and rest of things are same.
    And I updated little bit Controller class
    @RequestMapping (value={"/","/home"})
    public String homePage (Modelmap model)
    { model.addAttribute ("msg","hello");
    return "index";
    }
    @RequestMapping (value={"/list"})
    public String listUser (Modelmap model)
    {Same as before}
    When I run my project fitly @RequestMapping (value={"/","/home"})
    public String homePage (Modelmap model) It open the home page and when I click on login link in home page it open the login page and when I login now there is problem it open the home page not userlist after login,and when I click on login link in home page,now I will go to user list page.plz resolve this problem

  • hello websystique!
    I tried this example very best but i still meet some trouble:(
    I always have the situation off HTTP 404
    I was already set my tomcat . But it's still not work...
    Plz help me if you can ˊˋ

    • For 404 error in this project there are two reasons why it's showing. First: Maven dependecies are not added to project, to add them,
      right click on project -> properties -> deployment assembly -> add -> java build path entries -> maven dependencies -> finish
      Two: You have not installed and configured MySqlServer, here you can find out how to do it http://websystique.com/misc/how-to-setup-mysql-on-local-pc/
      Advice: application.properties file must match with your schema in MySql workbench

  • Hi ,
    Everything is running. But the problem is with password.
    when i try to login with abc125, not able to login. 'Invalid Username or password'.
    When i copy paste the encrypted password, able to login.
    How to fix this issue?
    Thanks

  • very nice example and perfect for what i am looking for. How to Run this Download code.. and also one query as i don't see any web.xml file in download project.
    getting Page not found error.. please help..

    Regards,
    Amol Sawant.

  • Hi websystique,
    Am getting HTTP Status 404 - /SpringMVCHibernateWithSpringSecurityExample/ am not getting what exactly going wrong. plz help me..?

    • @disqus_1vVzhnFrYC:disqus did you resolve this situation?
      I met the same wrong with HTTP Status 404

Share
Published by

Recent Posts

Spring Boot + AngularJS + Spring Data + JPA CRUD App Example

In this post we will be developing a full-blown CRUD application using Spring Boot, AngularJS, Spring Data, JPA/Hibernate and MySQL,…

7 years ago

Spring Boot Rest API Example

Spring Boot complements Spring REST support by providing default dependencies/converters out of the box. Writing RESTful services in Spring Boot…

7 years ago

Spring Boot WAR deployment example

Being able to start the application as standalone jar is great, but sometimes it might not be possible to run…

7 years ago

Spring Boot Introduction + hello world example

Spring framework has taken the software development industry by storm. Dependency Injection, rock solid MVC framework, Transaction management, messaging support,…

7 years ago

Secure Spring REST API using OAuth2

Let's secure our Spring REST API using OAuth2 this time, a simple guide showing what is required to secure a…

8 years ago

AngularJS+Spring Security using Basic Authentication

This post shows how an AngularJS application can consume a REST API which is secured with Basic authentication using Spring…

8 years ago