- Before we apply Spring Security on some service methods, let us open the UserServiceImpl class and add the following authorization: a super-user role to hradmin by adding ROLE_USER to its existing set of authorities; ROLE_ADMIN and ROLE_USER authorities to the "admin" account; and ROLE_USER authorization to the "sjctrags" account:
@Service("userService") public class UserServiceImpl implements UserService{ // refer to sources @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"); userC.add("ROLE_USER"); roles.put("sjctrags", userA); roles.put("admin", userB); roles.put("hradmin", userC); return roles.get(username); } }
- Impose access restrictions to our DepartmentService interface by applying role-based authorization using the Spring Security annotations @Secured, @PreAuthorize, and @PostAuthorize:
public interface DepartmentService { @Secured("ROLE_USER") public List<Department> readDepartments(); @Secured("ROLE_USER") public void addDepartment(DepartmentForm dept); @Secured("ROLE_ADMIN") public void removeDepartment(Integer deptId); @PreAuthorize("hasRole('USER') AND hasRole('HR')") public void updateDepartment(DepartmentForm dept, Integer id); @PreAuthorize("hasRole('USER') AND hasRole('HR')") public Department getDeptId(Integer id); }
Do not apply these changes to the implementation class.
- Create another @Controller that will contain restricted request methods. This class, RestrictedController, has a GET method that exposes banned departments once a ROLE_HR or ROLE_ADMIN accesses /deptbanned.html:
@Controller public class RestrictedController { @PreAuthorize("hasRole('HR') OR hasRole('ADMIN')") @RequestMapping("/deptbanned.html") public String bannedDepts(){ return "banned"; } }
- Create a view page for /deptbanned.html, which lists all banned departments. Update the views.properties and messages_en_US.properties for this additional view.
- Create a new security context, AppSecurityModelF, which contains the complete configuration for this recipe:
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled=true) public class AppSecurityModelF extends WebSecurityConfigurerAdapter { // refer to sources @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(authProvider()); 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) .and() .exceptionHandling() .accessDeniedPage("/access_denied.html"); http.csrf().disable(); } @Override public void configure(WebSecurity web) throws Exception { // refer to sources } @Bean public Md5PasswordEncoder md5PasswordEncoder(){ // refer to sources } @Bean public DaoAuthenticationProvider authProvider() { // refer to sources } }
- In order for the @Secured, @PreAuthorize, and @PostAuthorize annotations to be functional, the class-level annotation @EnableGlobalMethodSecurity must be configured, setting prePostEnable and securedEnabled to true.
- Lastly, create a view page /acces_denied.html, which is triggered if access to a restricted transaction is detected.
- Update SpringContextConfig to consider the new security model.
- Save all files. clean, build, and deploy the project.