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

  1. Inject the passwordEncoder from PasswordConfig class to ApplicationSecurityConfig
  2. 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();
    }
.
.
  1. In the above code we have added two roles – ADMIN and USER
  2. Both were authenticated to access the application.But to access the API the role should be ADMIN
      antMatchers("/api/**").hasRole("ADMIN")
    
  3. 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();
    }
}
  1. In the above piece of code we have defined two roles ADMIN and USER
  2. 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()
    .
    
  3. 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")
    .
    
  4. 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

  1. In the below code instead of using ROLES to authorize users to do something we use AUTHORITIES to allow user
  2. There are two ways to do this. One is by using hasAuthority in configure(HttpSecurity httpSecurity) method as below
  3. .
    @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")
    .
    
  4. 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";
    }
}

Comments are closed.