Instead of hardcoding the user details inside the security model, we will implement a service layer that will programmatically generate a username and password for the application:
- Let us create the UserService interface, as follows that will generate hardcoded data for the UserDetails:
public interface UserService { public String getUserCredentials(String username); public Set<String> getuserRoles(String username); }
- Save this file in our org.secured.mvc.service since this is just an application-based native service.
- Then, implement the interface through UserServiceImpl as follows:
@Service("userService") public class UserServiceImpl implements UserService{ @Override public String getUserCredentials(String username) { Map<String, String> credentials = new HashMap<>(); credentials.put("sjctrags", "sjctrags"); credentials.put("admin", "admin"); credentials.put("hradmin", "hradmin"); return credentials.get(username); } @Override public Set<String> getuserRoles(String username) { Map<String, Set<String>> roles = new HashMap<>(); Set<String> userA = new HashSet<>(); Set<String> userB = new HashSet<>(); Set<String> userC = new HashSet<>(); userA.add("ROLE_USER"); userB.add("ROLE_ADMIN"); userB.add("ROLE_USER"); userC.add("ROLE_HR"); userC.add("ROLE_ADMIN"); roles.put("sjctrags", userA); roles.put("admin", userB); roles.put("hradmin", userC); return roles.get(username); } }
- Now, create an implementation of UserDetailsService, which retrieves the user information through its username and provides this corresponding information to SecurityContext:
@Service("authUserService") public class AuthUserService implements UserDetailsService { @Autowired private UserService userService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { String password = userService.getUserCredentials(username); UserDetails user = new User(username, password, true, true, true, true, getAuthorities(username)); return user; } private Set<GrantedAuthority> getAuthorities(String username){ Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>(); for(String role : userService.getuserRoles(username)) { GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(role); authorities.add(grantedAuthority); } return authorities; } }
- Place this class inside the org.packt.secured.mvc.core.service package. We will be injecting this class inside our Spring Security container.
- Design now the AppSecurityModelD that will utilize UserDetails and UserDetailsService for storing and retrieving user identification:
@Configuration @EnableWebSecurity public class AppSecurityModelD extends WebSecurityConfigurerAdapter { // refer to sources @Autowired @Qualifier("authUserService") private UserDetailsService userDetailsService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService); auth.eraseCredentials(false); } @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/login**", "/after**").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login.html") .defaultSuccessUrl("/deptform.html") .failureUrl("/login.html?error=true") .successHandler(customSuccessHandler) .and() .logout().logoutUrl("/logout.html") .logoutSuccessHandler(customLogoutHandler); http.csrf().disable(); } @Override public void configure(WebSecurity web) throws Exception { // refer to sources } }
- For evidence that UserDetails have been injected into SecurityContext, utilize the CustomSuccessHandler and CustomLogoutHandler of the previous recipe. Modify a little bit the CustomLogoutHandler to extract the user credentials and roles using the java.security.Principal object:
@Component public class CustomLogoutHandler extends SimpleUrlLogoutSuccessHandler { private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); @Override public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { String targetUrl = targetUrl(authentication); // refer to sources redirectStrategy.sendRedirect(request, response, targetUrl); } protected String targetUrl(Authentication authentication) { UserDetails p = (UserDetails )authentication.getPrincipal(); String username = p.getUsername(); String password = p.getPassword(); String url = ""; Collection<? extends GrantedAuthority> authorities = p.getAuthorities(); List<String> roles = new ArrayList<String>(); for (GrantedAuthority a : authorities) { roles.add(a.getAuthority()); } if (isUser(roles)) { url = "/after_logout.html?message=" + " Thank your, " + username + " with password " + password +" and role(s):" + roles; } // refer to sources return url; } // refer to sources }
- Be sure to update SpringContextConfig by importing the new AppSecurityModelD and including the org.packt.secured.mvc.core.service in its @ComponentScan.
- Save all files. clean, build, and deploy the Maven project.