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.
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.
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>
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.
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 improve further our learning process.
In this post we will be developing a full-blown CRUD application using Spring Boot, AngularJS, Spring Data, JPA/Hibernate and MySQL,…
Spring Boot complements Spring REST support by providing default dependencies/converters out of the box. Writing RESTful services in Spring Boot…
Being able to start the application as standalone jar is great, but sometimes it might not be possible to run…
Spring framework has taken the software development industry by storm. Dependency Injection, rock solid MVC framework, Transaction management, messaging support,…
Let's secure our Spring REST API using OAuth2 this time, a simple guide showing what is required to secure a…
This post shows how an AngularJS application can consume a REST API which is secured with Basic authentication using Spring…
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");
}
}
Did you try to run it once with CSRF disabled?
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}
Simply Excellent
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';
Hello,
Can you please tell me, where should I add those lines of code ?
Hi,
You may refer to this post to understand how this can be done for your signup page.
When i submit the form, i m getting this error. why? what is the problem?
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
Is your customr UserDetailsService package path is included in component-scan?
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