ApplicationSecurityConfig.java
01 02 03 04 05 06 07 08 09 10 11 12 | @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)
01 02 03 04 05 06 07 08 09 10 11 12 13 14 | @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
01 02 03 04 05 06 07 08 09 10 11 | @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
1 2 3 4 5 6 7 8 | @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
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 | @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
01 02 03 04 05 06 07 08 09 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 | @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
01 02 03 04 05 06 07 08 09 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 | @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
01 02 03 04 05 06 07 08 09 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 | @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
01 02 03 04 05 06 07 08 09 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 | @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
01 02 03 04 05 06 07 08 09 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 | @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
01 02 03 04 05 06 07 08 09 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 | @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" ; } } |