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.

SpringSecurityHibernateRoleBasedLoginExample_img1

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

SpringSecurityHibernateRoleBasedLoginExample_img00
SpringSecurityHibernateRoleBasedLoginExample_img01

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.

SpringSecurityHibernateRoleBasedLoginExample_img1

Provide credentials of DBA

SpringSecurityHibernateRoleBasedLoginExample_img2

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

SpringSecurityHibernateRoleBasedLoginExample_img3

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

SpringSecurityHibernateRoleBasedLoginExample_img4

Provide wrong password & Submit.
SpringSecurityHibernateRoleBasedLoginExample_img6

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

SpringSecurityHibernateRoleBasedLoginExample_img7

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

SpringSecurityHibernateRoleBasedLoginExample_img8

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

SpringSecurityHibernateRoleBasedLoginExample_img9

Logout.

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

Download Source Code



References

If you like tutorials on this site, why not take a step further and connect me on Facebook , Google Plus & Twitter as well? I would love to hear your thoughts on these articles, it will help me improve further our learning process.

If you appreciate the effort I have put in this learning site, help me improve the visibility of this site towards global audience by sharing and linking this site from within and beyond your network. You & your friends can always link my site from your site on www.websystique.com, and share the learning.

After all, we are here to learn together, aren’t we?

  • http://www.beisbolicos.com Carlos De Luna Saenz

    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.

    • http://www.beisbolicos.com Carlos De Luna Saenz

      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”);

      }
      }

      • websystique

        Did you try to run it once with CSRF disabled?

        • http://www.beisbolicos.com Carlos De Luna Saenz

          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)…

        • http://www.beisbolicos.com Carlos De Luna Saenz

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

  • PA

    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}

  • MD MAHFUJUR RAHMAN SHEYAM

    Simply Excellent

  • Najoua

    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.

    • websystique

      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’;

      • Najoua

        Hello,
        Can you please tell me, where should I add those lines of code ?

        • websystique

          Hi,
          You may refer to this post to understand how this can be done for your signup page.

  • Khan

    When i submit the form, i m getting this error. why? what is the problem?

    • websystique

      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.

  • Pingback: Spring Security的一些资料 – 几度()

  • Muhammed Abdul

    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.

  • Maciej

    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

    • websystique

      Is your customr UserDetailsService package path is included in component-scan?

      • 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
        }

        }

  • Alejandro Fng

    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

    • websystique

      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.

  • Alejandro Fng

    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

  • Srinivas Inti

    Hi,

    I have few more queries on this post.

    First: displaying name

    How to display firstname instead of sso_id on the screen as I am using mobile number for ssoid?

    we are retrieving the sso_id by using the getpriciple method. here i would like retrieve the first name from the app_user table.
    note:I wanted user to login with ssoid only.

    Second:redirecting

    how do I able redirect to the same page where user left earlier after success login?

    Third: logout
    Wanted to redirect to home page after user logout.

    Fourth: whats your name? :-)

    Thanks in advance…it will be very helpful if you help me on the above queries !!!

    Regards,
    Srini.

    • Srinivas Inti

      Hi,

      I fixed the first issue. could you please update on the rest?

      Regards,
      Srini

      • websystique

        Hi Inti,

        For your other questions, you need to focus n RedirectStrategy which is used in this post. You can get the idea from here.

  • Srinivas Inti

    Hi,

    I integrated my existing code into this example. after admin logged in, i have one POST call which is not working..giving an error saying “WARNING: Request method ‘POST’ not supported”

    Code snippet is below:

    added following matcher in the spring configuration.

    .antMatchers(“/agentpage/**”).access(“hasRole(‘ADMIN’)”)
    .antMatchers(“/myloadorders”).access(“hasRole(‘ADMIN’)”)

    in the controller

    @RequestMapping(value = “/myloadorders”, method = RequestMethod.POST)
    public @ResponseBody List getMyLoadOrders(@RequestBody OrderDetails orderDetails) {}

    and invoking the above rest method from the angular js

    var responsePromise = $http.post(“myloadorders”, dataObject, {});

    could you please help me to invoke POST method.. I tried all possible ways to pass csrf token…its not working!!!

    • websystique

      Hi Srinivas,

      I might need some info from you in order to find the issue.

      1) I suppose your angular setup itself works fine [without even including Spring security]?

      For any doubts, you might want to refer to : AngularJS $http Service communication

      2) Can you enable the Spring debug mode in you app and send me complete logs.You may use CONTACT US page for the same.

      3) Did you try to switch-off CSRF to see if this is causing problem for you?
      For that you can do following

      a) Disable CSRF in SecurityConfiguration.java

      replace
      .and().csrf().and().exceptionHandling().accessDeniedPage(“/Access_Denied”);
      By
      .and().exceptionHandling().accessDeniedPage(“/Access_Denied”).and().csrf().disable();

      b) Comment CSRF hidden parameter in JSP’s [login.jsp or any jsp you might be using]

      Comment this line

      Thanks for providing info on these points. I do recall someone mentioned about somewhat similar issue in past, i will have a look at it. It is possible that i am missing something here which you discovered.

      • Srinivas Inti

        Hi,

        first all thank you very much for your efforts….

        I fixed the issue by overriding the configureMessageConverters() in HelloWorldConfiguration class as below.

        @Override
        public void configureMessageConverters(List<HttpMessageConverter> converters) {
        GsonHttpMessageConverter msgConverter = new GsonHttpMessageConverter();
        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        msgConverter.setGson(gson);
        converters.add(msgConverter);
        }

        I dont think it is due to csrf enable in our spring security.however, I disabled my csrf in my application for now. I tried overriding extendMessageConverters method which is been mentioned in the below link. unfortunately, that piece of code is giving complication errors(could be due my jar dependencies)
        http://websystique.com/springmvc/spring-mvc-requestbody-responsebody-example/

        I migrated my whole xml based code to java based by taking inspiration from you.
        Thank you…I am enjoying your portal…all the best…

        Regards,
        Srinivas.

        • websystique

          Hi Srinivas,

          Thanks a lot. I learned something new from your answer. I am sure it will help others as well.

    • websystique

      Hi Srinivas,

      Additionally look at reply from Erwin on this post. It might help you.

  • Srinivas Inti

    Thank you very much…your code really helped me…its working as expected… :)

  • Pingback: Spring Security 4 Hibernate Integration Annotation+XML Example - WebSystique()