ApplicationSecurityConfig.java
@Configuration @EnableWebSecurity public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity httpSecurity) throws Exception{ httpSecurity.authorizeRequests() .anyRequest() .authenticated() .and() .httpBasic(); } }
Whitelisting some URLs(index, js and CSS files)
@Configuration @EnableWebSecurity public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity httpSecurity) throws Exception{ httpSecurity.authorizeRequests() //Whitelisting URLS .antMatchers("/", "index", "/css/*", "/js/*").permitAll() .anyRequest() .authenticated() .and() .httpBasic(); } }
Authentication with password with no encryption
@Override @Bean protected UserDetailsService userDetailsService() { UserDetails mugilUsrBuilder = User.builder() .username("Mugil") .password("{noop}password") .roles("ADMIN") .build(); return new InMemoryUserDetailsManager(mugilUsrBuilder); }
If {noop} is not used in password Spring security would throw an error asking to encode the password with password encoder as below.
java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id “null”
Using Password Simple Encoder
PasswordConfig.java
@Configuration public class PasswordConfig { @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(10); } }
ApplicationSecurityConfig.java
- Inject the passwordEncoder from PasswordConfig class to ApplicationSecurityConfig
- Encode the password using instance of injected encoder in ApplicationSecurityConfig
@Autowired private PasswordEncoder passwordEncoder; @Autowired public ApplicationSecurityConfig(PasswordEncoder passwordEncoder) { this.passwordEncoder = passwordEncoder; } @Override @Bean protected UserDetailsService userDetailsService() { UserDetails mugilUsrBuilder = User.builder() .username("Mugil") .password(this.passwordEncoder.encode("password")) .roles("ADMIN") .build(); return new InMemoryUserDetailsManager(mugilUsrBuilder); }
Allowing Access to API based on Role – Authorization
ApplicationSecurityConfig.java
@Configuration @EnableWebSecurity public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter { . . @Override @Bean //Authentication protected UserDetailsService userDetailsService() { UserDetails adminUsrBuilder = User.builder() .username("admin") .password(this.passwordEncoder.encode("password")) .roles("ADMIN") .build(); UserDetails regularUsrBuilder = User.builder() .username("user") .password(this.passwordEncoder.encode("password")) .roles("USER") .build(); return new InMemoryUserDetailsManager(adminUsrBuilder, regularUsrBuilder); } @Override //Authorization protected void configure(HttpSecurity httpSecurity) throws Exception{ httpSecurity.authorizeRequests() //Whitelisting URLS .antMatchers("/", "index", "/css/*", "/js/*").permitAll() .antMatchers("/api/**").hasRole("ADMIN") .anyRequest() .authenticated() .and() .httpBasic(); } . .
- In the above code we have added two roles – ADMIN and USER
- Both were authenticated to access the application.But to access the API the role should be ADMIN
antMatchers("/api/**").hasRole("ADMIN")
- If the user with Role USER try to access API then it would end up in 403 – Forbidden Error
Access Allowed
Forbidden Access
Allowing Access based on 2 Different Role
ApplicationSecurityConfig.java
@Configuration @EnableWebSecurity public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter { . . @Override @Bean protected UserDetailsService userDetailsService() { UserDetails adminUsrBuilder = User.builder() .username("admin") .password(this.passwordEncoder.encode("password")) .roles("ADMIN") .build(); UserDetails regularUsrBuilder = User.builder() .username("user") .password(this.passwordEncoder.encode("password")) .roles("USER") .build(); return new InMemoryUserDetailsManager(adminUsrBuilder, regularUsrBuilder); } @Override protected void configure(HttpSecurity httpSecurity) throws Exception{ httpSecurity.csrf().disable() .authorizeRequests() //Whitelisting URLS .antMatchers("/", "index", "/css/*", "/js/*").permitAll() .antMatchers(HttpMethod.GET,"/api/**").permitAll() .antMatchers(HttpMethod.DELETE,"/api/**").hasRole("ADMIN") .antMatchers(HttpMethod.PUT,"/api/**").hasRole("ADMIN") .antMatchers(HttpMethod.POST,"/api/**").hasRole("ADMIN") .anyRequest() .authenticated() .and() .httpBasic(); } }
- In the above piece of code we have defined two roles ADMIN and USER
- Those with USER role can access the API with HTTP Get Method. That means both ADMIN and USER role could access all the API using GET method
. .antMatchers(HttpMethod.GET,"/api/**").permitAll() .
-
Those with ADMIN role can access the API with HTTP POST, DELETE and PUT Method which corresponds to Create, Delete and Update as per Open API Specifiaction.
. .antMatchers(HttpMethod.DELETE,"/api/**").hasRole("ADMIN") .antMatchers(HttpMethod.PUT,"/api/**").hasRole("ADMIN") .antMatchers(HttpMethod.POST,"/api/**").hasRole("ADMIN") .
- The above could be cross checked by changing postman call with HttpMethods and Credentials
StudentsService.java
@RestController @RequestMapping("/api/v1/students") public class StudentsService { @Autowired StudentRepo studentRepo; @GetMapping(path="{studentId}") public Student getStudentById(@PathVariable("studentId") String studentId){ return studentRepo.getStudentById(studentId); } @GetMapping public List<Student> getStudentList(){ return studentRepo.getStudentsList(); } @PutMapping public String updateStudent(@RequestBody Student student){ return studentRepo.updateStudent(student); } @PostMapping public String addStudent(@RequestBody Student student){ if(studentRepo.addStudents(student)) return "Student with Id " + student.getStudentId() + " added successfully"; else return "Error:Unable to create Student"; } @DeleteMapping(path="{studentId}") public String deleteStudent(@PathVariable("studentId") String studentId){ studentRepo.deleteStudent(studentId); return "Student Deleted Successfully"; } }
Allowing Access based on 2 Different Authority(or)Permission
- In the below code instead of using ROLES to authorize users to do something we use AUTHORITIES to allow user
- There are two ways to do this. One is by using hasAuthority in configure(HttpSecurity httpSecurity) method as below
-
. @EnableGlobalMethodSecurity(prePostEnabled = true) . .antMatchers(HttpMethod.POST,"/api/v1/students/").hasAuthority("WRITE") .antMatchers(HttpMethod.DELETE,"/api/v1/students/**").hasAuthority("WRITE") .antMatchers(HttpMethod.PUT,"/api/v1/students/").hasAuthority("WRITE") .
- Other is by using @preauthorize annotation to decide the methods
which could be allowed access to. . @PreAuthorize("hasAuthority('READ')") public List
getStudentList(){ . @PreAuthorize("hasAuthority('WRITE')") public String updateStudent(@RequestBody Student student){ . .
ApplicationSecurityConfig.java
Using hasAuthority
@Configuration @EnableWebSecurity public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private PasswordEncoder passwordEncoder; @Autowired public ApplicationSecurityConfig(PasswordEncoder passwordEncoder) { this.passwordEncoder = passwordEncoder; } @Override @Bean protected UserDetailsService userDetailsService() { GrantedAuthority[] arrGrantedAuthAdmin = {new SimpleGrantedAuthority("READ"), new SimpleGrantedAuthority("WRITE")}; GrantedAuthority[] arrGrantedAuthUser = {new SimpleGrantedAuthority("READ")}; UserDetails adminUsrBuilder = User.builder() .username("admin") .password(this.passwordEncoder.encode("password")) .authorities("READ", "WRITE") .build(); UserDetails regularUsrBuilder = User.builder() .username("user") .password(this.passwordEncoder.encode("password")) .authorities("READ") .build(); return new InMemoryUserDetailsManager(adminUsrBuilder, regularUsrBuilder); } @Override protected void configure(HttpSecurity httpSecurity) throws Exception{ httpSecurity.csrf().disable() .authorizeRequests() .antMatchers("/", "index", "/css/*", "/js/*").permitAll() .antMatchers(HttpMethod.POST,"/api/v1/students/").hasAuthority("WRITE") .antMatchers(HttpMethod.DELETE,"/api/v1/students/**").hasAuthority("WRITE") .antMatchers(HttpMethod.PUT,"/api/v1/students/").hasAuthority("WRITE") .anyRequest() .authenticated() .and() .httpBasic(); } }
ApplicationSecurityConfig.java
Using @PreAuthorize and EnableGlobalMethodSecurity
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private PasswordEncoder passwordEncoder; @Autowired public ApplicationSecurityConfig(PasswordEncoder passwordEncoder) { this.passwordEncoder = passwordEncoder; } @Override @Bean protected UserDetailsService userDetailsService() { GrantedAuthority[] arrGrantedAuthAdmin = {new SimpleGrantedAuthority("READ"), new SimpleGrantedAuthority("WRITE")}; GrantedAuthority[] arrGrantedAuthUser = {new SimpleGrantedAuthority("READ")}; UserDetails adminUsrBuilder = User.builder() .username("admin") .password(this.passwordEncoder.encode("password")) .authorities("READ", "WRITE") .build(); UserDetails regularUsrBuilder = User.builder() .username("user") .password(this.passwordEncoder.encode("password")) .authorities("READ") .build(); return new InMemoryUserDetailsManager(adminUsrBuilder, regularUsrBuilder); } @Override protected void configure(HttpSecurity httpSecurity) throws Exception{ httpSecurity.csrf().disable() .authorizeRequests() .antMatchers("/", "index", "/css/*", "/js/*").permitAll() .anyRequest() .authenticated() .and() .httpBasic(); } }
StudentsService.java
@RestController @RequestMapping("/api/v1/students") public class StudentsService { @Autowired StudentRepo studentRepo; @GetMapping(path="{studentId}") public Student getStudentById(@PathVariable("studentId") String studentId){ return studentRepo.getStudentById(studentId); } @GetMapping @PreAuthorize("hasAuthority('READ')") public List<Student> getStudentList(){ return studentRepo.getStudentsList(); } @PutMapping @PreAuthorize("hasAuthority('WRITE')") public String updateStudent(@RequestBody Student student){ return studentRepo.updateStudent(student); } @PostMapping @PreAuthorize("hasAuthority('WRITE')") public String addStudent(@RequestBody Student student){ if(studentRepo.addStudents(student)) return "Student with Id " + student.getStudentId() + " added successfully"; else return "Error:Unable to create Student"; } @DeleteMapping(path="{studentId}") @PreAuthorize("hasAuthority('WRITE')") public String deleteStudent(@PathVariable("studentId") String studentId){ studentRepo.deleteStudent(studentId); return "Student Deleted Successfully"; } }