- Before we start the main recipe, let us add the following tables in our hrs schema:
- The userdetails class will contain the usual general user information, while logindetails contains the username, password, and encrypted password of each user. On the other hand, role_permission contains all the roles and access permissions of each user in logindetails. A Permission is defined as the allowable CRUD transaction to be performed by a user, such as READ, WRITE, VIEW, DELETE, and REPORT, which is different from the usual Role. A role ADMIN, for instance, can have ADMIN_UPDATE and ADMIN_DELETE permissions.
- With the newly added tables, generate the model for each table and create a typical DAO interface that will add, retrieve, update, and delete the records:
public interface LoginDao { public List<Role> getUserRoles(); public Role getUserRole(int id); public List<Permission> getPermissions(); public Permission getPermission(int id); public List<RolePermission> getUserGrantedAuthority(int userId); public List<AccountLogin> getUsers(); public AccountLogin getUser(String username); }
- Implement a @Repository implementation of this interface using SimpleJdbcInsert and JdbcTemplate.
- Inside the package org.packt.secured.mvc.core.user, create custom UserDetails that will store all logindetails with some additional transactions, such as sorting the GrantedAuthority and username/password validation:
public class AppUserDetails implements UserDetails { private String password; private final String username; private final Set<GrantedAuthority> authorities; private final boolean accountNonExpired; private final boolean accountNonLocked; private final boolean credentialsNonExpired; private final boolean enabled; @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; } // refer to sources public AppUserDetails (String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) { if (((username == null) || "".equals(username)) || (password == null)) { throw new IllegalArgumentException( "Empty values are not allowed"); } this.username = username; this.password = password; // refer to sources } private static SortedSet<GrantedAuthority> sortAuthorities(Collection<? extends GrantedAuthority> authorities) { SortedSet<GrantedAuthority> sortedAuthorities = new TreeSet<GrantedAuthority>( new AuthorityComparator()); for (GrantedAuthority grantedAuthority : authorities) { sortedAuthorities.add(grantedAuthority); } return sortedAuthorities; } private static class AuthorityComparator implements Comparator<GrantedAuthority>, Serializable { public int compare(GrantedAuthority g1, GrantedAuthority g2) { // refer to sources return g1.getAuthority() .compareTo(g2.getAuthority()); } } }
- Afterwards, create another UserDetailsService inject and use LoginDao to retrieve the user details for the custom AppUserDetails:
@Service("authJdbcUserService") public class AuthJdbcUserService implements UserDetailsService{ @Autowired private LoginDao loginDaoImpl; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { AccountLogin login = loginDaoImpl.getUser(username); // refer to sources UserDetails user = new AppUserDetails(login.getUsername(), login.getEncPassword(),true, true, true, true, getAuthorities(username, login)); return user; } private Set<GrantedAuthority> getAuthorities(String username, AccountLogin login){ List<RolePermission> roleperms = loginDaoImpl.getUserGrantedAuthority(login.getId()); // refer to sources return authorities; }
- Populate logindetails with the encoded password using the same injected PasswordEncoder:
- Create AppSecurityModelE2, having authJdbcUserService injected as the new UserDetailsService:
@Configuration @EnableWebSecurity public class AppSecurityModelE2 extends WebSecurityConfigurerAdapter{ // refer to sources @Autowired @Qualifier("authJdbcUserService") private UserDetailsService userDetailsService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(authProvider()); auth.eraseCredentials(false); } // refer to sources @Bean public DaoAuthenticationProvider authProvider() { DaoAuthenticationProvider daoProvider = new DaoAuthenticationProvider(); daoProvider.setPasswordEncoder(md5PasswordEncoder()); daoProvider.setUserDetailsService(userDetailsService); ReflectionSaltSource saltHash = new ReflectionSaltSource(); saltHash.setUserPropertyToUse("username"); daoProvider.setSaltSource(saltHash); return daoProvider; } }
- Update SpringContextConfig to consider the new security model.
- Save all files. clean, build, and deploy the project.