Application Requirement
I’m working on some personal project that handles 4 different user types (e.g., students, instructors, registrar employees, security personnel). Each user type is stored in a separate user table. With these, I require four (4) login forms.
Software Requirement
For this blog, the following are used.
- IntelliJ IDEA (optional)
- JDK 8
- Spring Initizr (http://start.spring.io/)
- Spring Boot 1.4.2.RELEASE
- Thymeleaf
- Spring Security
The codes
Login Form
Using Thymeleaf
, we have this studentLogin.html
file. Now notice line # 17. We specify the type of user using a hidden field.
For other user types, you may need to specify different user types in a similar way.
[wp_ad_camp_2]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head lang="en"> <title>Turreta.com - Spring Core Online Tutorial</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <link th:href="@{/webjars/bootstrap/3.3.7/css/bootstrap.min.css}" rel="stylesheet" media="screen"/> <script th:href="@{/webjars/jquery/3.1.1/css/jquery.min.js}"></script> </head> <body> <div class="container"> <div class="row"> <div class="col-md-5 col-md-offset-4"> <h2>Login</h2> <div> <form class="form-horizontal" th:object="${loginForm}" th:action="@{/login}" method="post"> <input type="hidden" name="userType" id="userType" value="studentUser"/> <div th:if="${param.error}" class="alert alert-danger"> <p>Invalid username and password.</p> </div> <div th:if="${param.logout}" class="alert alert-success"> <p>You've been logged out.</p> </div> <div class="form-group" th:class="${param.error} ? 'form-group has-error' : 'form-group'"> <label class="col-sm-2 control-label">Username:</label> <div class="col-sm-10"> <input type="text" class="form-control" name="username" id="username"/> </div> </div> <div class="form-group" th:class="${param.error} ? 'form-group has-error' : 'form-group'"> <label class="col-sm-2 control-label">Password:</label> <div class="col-sm-10"> <input type="password" class="form-control" name="password" id="password"/> </div> </div> <button type="submit" class="btn btn-default">Submit</button> </form> </div> </div> </div> </div> </body> </html> |
Custom Filter
We need a filter to get the additional parameter from the HttpServletRequest
and set it as a HttpSession
attribute.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | package com.turreta.bbb.config; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.web.filter.GenericFilterBean; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class CustomUsernamePasswordAuthenticationFilter extends GenericFilterBean { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { if(servletRequest instanceof HttpServletRequest) { HttpServletRequest r = (HttpServletRequest)servletRequest; String loginType = r.getParameter("userType"); r.getSession().setAttribute("userType", loginType); System.out.println("Filter" + loginType); } filterChain.doFilter(servletRequest, servletResponse); } } |
Then include this new filter in the filter chain by modifying your security configuration Java file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | package com.turreta.bbb.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.ProviderManager; import org.springframework.security.config.annotation.ObjectPostProcessor; 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.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import java.util.ArrayList; import java.util.List; @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Bean @Autowired public AuthenticationManager authenticationManager(UserDetailsService securityUserDetailsService) throws Exception { ObjectPostProcessor<Object> objectPostProcessor = new ObjectPostProcessor<Object>() { public <T> T postProcess(T object) { return object; } }; AuthenticationManagerBuilder m = new AuthenticationManagerBuilder(objectPostProcessor); m.userDetailsService(securityUserDetailsService); return m.build(); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().antMatchers("/**/favicon.ico").permitAll() .and().authorizeRequests().antMatchers("/webjars/**").permitAll() .and().authorizeRequests().antMatchers("/static/css").permitAll() .and().authorizeRequests().antMatchers("/js").permitAll() .and().formLogin().loginPage("/login").permitAll() .and().logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/login") .and().authorizeRequests().antMatchers("/student/**").authenticated(); http.addFilterBefore(new CustomUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); } } |
UserDetailService Implementation
You would then retrieve that parameter using RequestContextHolder
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | import com.yyy.xxx.service.user.AppUserService; import org.springframework.beans.factory.annotation.Autowired; 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.util.StringUtils; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; public class SecurityUserDetailsServiceImpl implements UserDetailsService { private static String USER_TYPE = "userType"; @Autowired private AppUserService appUserService; @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { /** * userType determines which user type to query for. */ String userType = (String) RequestContextHolder.getRequestAttributes().getAttribute(USER_TYPE, RequestAttributes.SCOPE_SESSION); if(StringUtils.isEmpty(userType)) { return null; } SecurityUser secUser = null; switch(userType) { case "studentUser": secUser = appUserService.searchStudentUserByUsername(s); break; case "instructorUser": secUser = appUserService.searchInstructorUserByUsername(s); break; case "registrarUser": secUser = appUserService.searchRegistrarUserByUsername(s); break; default: secUser = null; } return secUser; } } |
SecurityUser give error “SecurityUser cannot be resolved to a type”
at SecurityUser secUser=null
Hello Moreshwar,
Could you provide me sample codes with same error in GitHub so that I can look into the problem?