Categories: spring-security

Spring Security 4 Hibernate Role Based Login Example

This post shows how to use role based login in Spring Security 4 using Hibernate setup. That means redirecting users to different URLs upon login according to their assigned roles, this time along with Hibernate setup. Let’s get going.

This post complements the post Spring Security 4 Hibernate Annotation Example, and simply adds the Role based login functionality to that post. Since this post is 99% same in all regards to post Spring Security 4 Hibernate Annotation Example except few changes, we will not repeat that code here. Instead, only the changes are shown below.

Step 1: Create a NEW Custom Success Handler

Goal of this class is to provide custom redirect functionality we are looking for.

package com.websystique.springsecurity.configuration;

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

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

import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

@Component
public class CustomSuccessHandler extends SimpleUrlAuthenticationSuccessHandler{

 private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
 
    @Override
    protected void handle(HttpServletRequest request, 
      HttpServletResponse response, Authentication authentication) throws IOException {
        String targetUrl = determineTargetUrl(authentication);
 
        if (response.isCommitted()) {
            System.out.println("Can't redirect");
            return;
        }
 
        redirectStrategy.sendRedirect(request, response, targetUrl);
    }
    
    protected String determineTargetUrl(Authentication authentication) {
     String url="";
     
        Collection<? extends GrantedAuthority> authorities =  authentication.getAuthorities();
        
  List<String> roles = new ArrayList<String>();

  for (GrantedAuthority a : authorities) {
   roles.add(a.getAuthority());
  }

  if (isDba(roles)) {
   url = "/db";
  } else if (isAdmin(roles)) {
   url = "/admin";
  } else if (isUser(roles)) {
   url = "/home";
  } else {
   url="/accessDenied";
  }

  return url;
    }
 
    public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
        this.redirectStrategy = redirectStrategy;
    }
    protected RedirectStrategy getRedirectStrategy() {
        return redirectStrategy;
    }
    
 private boolean isUser(List<String> roles) {
  if (roles.contains("ROLE_USER")) {
   return true;
  }
  return false;
 }

 private boolean isAdmin(List<String> roles) {
  if (roles.contains("ROLE_ADMIN")) {
   return true;
  }
  return false;
 }

 private boolean isDba(List<String> roles) {
  if (roles.contains("ROLE_DBA")) {
   return true;
  }
  return false;
 }

}

Notice how we are extending Spring SimpleUrlAuthenticationSuccessHandler class and overriding handle() method which simply invokes a redirect using configured RedirectStrategy [default in this case] with the URL returned by the user defined determineTargetUrl method. This method extracts the Roles of currently logged in user from Authentication object and then construct appropriate URL based on there roles. Finally RedirectStrategy , which is responsible for all redirections within Spring Security framework , redirects the request to specified URL.

Step 2: Register Custom Success Handler with [existing] Security Configuration

package com.websystique.springsecurity.configuration;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

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

 @Autowired
 CustomSuccessHandler customSuccessHandler;
 
 
 @Autowired
 public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
  auth.userDetailsService(userDetailsService);
 }
 
 @Override
 protected void configure(HttpSecurity http) throws Exception {
   http.authorizeRequests()
    //.antMatchers("/", "/home").permitAll()
    .antMatchers("/", "/home").access("hasRole('USER')")
    .antMatchers("/admin/**").access("hasRole('ADMIN')")
    .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
    //.and().formLogin().loginPage("/login")
    .and().formLogin().loginPage("/login").successHandler(customSuccessHandler)
    .usernameParameter("ssoId").passwordParameter("password")
    .and().csrf()
    .and().exceptionHandling().accessDeniedPage("/Access_Denied");
 }

}

What changes compare to previous Hibernate post is an extra call to successHandler() as highlighted below:
formLogin().loginPage("/login").successHandler(customSuccessHandler).
Look at successHandler. This is the class responsible for eventual redirection based on any custom logic, which in our case will be to redirect the user [to home/admin/db ] based on his role [USER/ADMIN/DBA].

Additionally, we have also protected the home page, under USER role, to make example more realistic.

That’s it. Just add this class in configuration package and register it as success-handler with Security Configuration (as shown above).

Above security configuration in XML configuration format would be:

<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
    http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd">
     
    <http auto-config="true" >
        <intercept-url pattern="/" access="permitAll" />
        <intercept-url pattern="/home" access="permitAll" />
        <intercept-url pattern="/admin**" access="hasRole('ADMIN')" />
        <intercept-url pattern="/dba**" access="hasRole('ADMIN') and hasRole('DBA')" />
        <form-login  login-page="/login" 
                     username-parameter="ssoId" 
                     password-parameter="password" 
                     authentication-success-handler-ref="customSuccessHandler"
                     authentication-failure-url="/Access_Denied" />
        <csrf/>
    </http>
 
    <authentication-manager >
        <authentication-provider user-service-ref="customUserDetailsService"/>
    </authentication-manager>
     
    <beans:bean id="customUserDetailsService" class="com.websystique.springsecurity.service.CustomUserDetailsService" />
    <beans:bean id="customSuccessHandler"     class="com.websystique.springsecurity.configuration.CustomSuccessHandler" />
    
</beans:beans>

Project Directory Structure


Build and Deploy the application

Download the code and build the war (either by eclipse/m2eclipse) 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.

FYI, We will be using same Schema/Users/Roles as defined in previous post.

Open browser and goto localhost:8080/SpringSecurityHibernateRoleBasedLoginExample/, you will be prompted for login.

Provide credentials of DBA

submit, you will be taken directly to /db page as the logged-in user has DBA role. ROLE-BASED-LOGIN

Now logout, and fill-in credentials of a USER role.

Provide wrong password & Submit.

Provide correct USER role credentials, you will be redirected to home page.

Now try to access admin page.You should see AccessDenied page.

Now logout and login with ADMIN credentials, you will be taken to /admin URL.

Logout.


That’s it. Next post shows you Password Encoding in Spring Security using BCryptPasswordEncoder.

Download Source Code


References

View Comments

  • I really don't know what i'm missing, but i am having a trouble restoring the view (don't matter if i put any user on the for or not).
    @Configuration
    @EnableWebSecurity
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

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

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

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

    http.authorizeRequests()
    .antMatchers("/", "/faces/*").permitAll()
    .antMatchers("/faces/admin/**").access("hasRole('ADMIN')")
    .antMatchers("/faces/reports/**").access("hasRole('SUP_ADMIN')")
    .antMatchers("/faces/llamada/**").access("hasRole('OPERADOR')")
    .and().formLogin().loginPage("/faces/index.xhtml").successHandler(customSuccessHandler)
    .usernameParameter("loginName").passwordParameter("password")
    .and().csrf()
    .and().exceptionHandling().accessDeniedPage("/faces/Access_Denied.xhtml");

    }
    }
    I am using (also) primefaces... i want to incorporate it and may be i put some noise in there with the faces...
    Thanks a lot.

    • My Security configuration:
      @Configuration
      @EnableWebSecurity
      public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

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

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

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

      http.authorizeRequests()
      .antMatchers("/", "/faces/*").permitAll()
      .antMatchers("/faces/admin/**").access("hasRole('ADMIN')")
      .antMatchers("/faces/reports/**").access("hasRole('SUP_ADMIN')")
      .antMatchers("/faces/llamada/**").access("hasRole('OPERADOR')")
      .and().formLogin().loginPage("/faces/index.xhtml").successHandler(customSuccessHandler)
      .usernameParameter("loginName").passwordParameter("password")
      .and().csrf()
      .and().exceptionHandling().accessDeniedPage("/faces/Access_Denied.xhtml");

      }
      }

        • Seems it worked... i put the csrf disabling at the top, but i didn't see that the .and().csrf() was on bottom... thanks

        • Same result... what catches my atttention is that is trying to display (i guess) and i don't know where that viewId came from... (you can see the exception handling and it has /faces/ befeore it)...

  • Hello,

    May I request you to add following "maven-compiler-plugin" configuration, so that we've option of quickly changing the java version there ?

    4.1.6.RELEASE
    4.0.1.RELEASE
    4.3.6.Final
    5.1.31
    1.7

    This should be in tag

    maven-compiler-plugin

    ${java.version}
    ${java.version}

  • Hello,
    Thank you so much for this interesting tutorial, it's exactly what I was looking for.
    In addition to that I was trying to add a sign up page. I added a link on the login page.
    I worked on the controller, service... But, still I have error 404
    Can you please suggest a project directory structure that could help ?
    Thanks in advance.

    • Hi Najoua,

      Sorry for late reply. During signup, you should insert a record into join table [APP_USER_USER_PROFILE], with role 'USER' as default.

      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='kenny' and profile.type='DBA';

    • Hi Khan, sorry i missed to reply your message. I remember someone complaining about this error before. It could be related to expressions in jsp being not evaluated. I would suggest including in your JSP and try again.

  • Hi, when I use this article's code in 'Spring 4 MVC + Hibernate4 Many-to-many JSP Example using annotation with join table + MySQL + Maven', I got the problem below:
    no matter which user I choosed to login, I got to return the Access_Denied page,but the url is redirect the right url base the user.
    even I changed the
    @Override

    public void configureViewResolvers(ViewResolverRegistry registry)

    to

    @Bean

    public ViewResolver viewResolver()

    still get the same problem.

  • Hi, Great Tutorials and i have learned a lot from this website. Thank you :)

    I have this example running on my machine , now i want to use the XML style configurations for web.xml, spring and spring security. I am trying since a long time, but its getting complicated and I end up getting the code on the page instead of the compiled code.

    Could you please share the same example with complete web.xml , spring dispatcher xml and security xmls? Please..

    Thank you.

  • Hi All,

    I am trying to implement this solution to your previous project on which I am basing my program:
    http://websystique.com/spring-security/spring-security-4-hello-world-annotation-xml-example/

    I have added the modified CustomUserDetailsService as below:

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

    @Autowired
    TenantService tenantService;

    @Transactional(readOnly = true)
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {

    //my own class that has email, password and one role (admin or user)
    Tenant tenant = tenantService.loadByMail(email);

    List authorities = new ArrayList();
    authorities.add(new SimpleGrantedAuthority(tenant.getRole()));

    return new org.springframework.security.core.userdetails.User(tenant.getEmail(), tenant.getPassword(), true, true, true, true, authorities);

    }

    And the security config class;

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    UserDetailsService userDetailsService;

    @Autowired
    public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService);
    auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests().antMatchers("/", "/index").permitAll().antMatchers("/Admin/**")
    .access("hasRole('ADMIN')").antMatchers("/User/**").access("hasRole('ADMIN') or hasRole('USER')")
    .and().formLogin();

    }

    }

    When I try to start the application I get:

    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'securityConfig': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: org.springframework.security.core.userdetails.UserDetailsService kamienica.configuration.SecurityConfig.userDetailsService; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.core.userdetails.UserDetailsService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

    Maybe you will know how to properly autowire the UserDetailsService ....

    I appreciate any help,

    Maciej

      • Within appConfig there is component scan set to kamienica.
        All my packages follow the same pattern: -> kamienica.**

        @Configuration
        @EnableWebMvc
        @ComponentScan(basePackages = "kamienica")
        public class AppConfig {

        @Bean
        public ViewResolver viewResolver() {
        //body
        }

        @Bean
        public MessageSource messageSource() {
        //body
        }

        }

  • Please help me. i get the web module error. I changed the java build path, java compiler, project facets to correspond. is there anything else i should change? im using jdk 1.8

    • Hi Alejandro, First you need to fix build-path for your project . project->right-click->properties->java Build Path->Libraries. Here you will provide the java version you have (as you mentioned 1.8). Once done, click ok, and then again project->right-click->Maven->Update project. Here select your project and OK.

      Additionally, it seems to me that your tomcat setup is not proper: Please follow this link to configure it properly.

      You should be good to go.If you still face any issue, let me know.

  • Please help me. i get the web module error. I changed the java build path, java compiler, project facets to correspond. is there anything else i should change? im using jdk 1.8

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