Skip to content

Sohaibgit/spring_security_demo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

30 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

spring_security_demo

Learning Path

For Complete Understanding, Read through docs and code of all branches in below order \

  1. role_based_authentication
  2. permission_based_authentication
  3. database_authentication
  4. java_json_web_tokens

spring-boot-starter-security
Just by adding spring security dependency, we get a default login page with username and password. To access any resource we need to provide username and password.
username: user
password: generated by spring security

Implement Basic Authorization

  1. Create a class, annotate it with @Configuration and @EnableWebSecurity and extend WebSecurityConfigurerAdapter.
  2. Overrid configure(HttpSecurity http) and add below code.
   @Override
   protected void configure(HttpSecurity http) throws Exception {
       // any request should be authenticated i.e user should provide username and password and mechanism should be basic auth
       // antMatchers --> to allow access to home page like index to all users without any username and password
       // antMatchers("/api/**").hasRole(STUDENT.name()) --> Only allow user who have a role of STUDENT to access this API

       http.authorizeRequests()
               .antMatchers("/", "index", "/css/*", "/js/*")
               .permitAll()
               .anyRequest()
               .authenticated()
               .and()
               .httpBasic();
   }

Code Explaination

http.authorizeRequests("authorize the request")
.antMatchers("/", "index", "/css/*", "/js/*")
.permitAll("means above listed urls in antMatchers are now whitelisted means they are accessible without username and password")
.anyRequest("any request")
.authenticated("should be authenticated")
.and("and also")
.httpBasic("mechanism should be basic auth");

Create a User

  1. Override below method and annotate it with @Bean to create a user
    @Override
    @Bean
    protected UserDetailsService userDetailsService() {
        UserDetails mariumUser = User.builder()
                .username("student")
                .password("student")
                .roles("STUDENT")
                .build();

        return new InMemoryUserDetailsManager(mariumUser);
    }

PasswordEncoder
Spring Security uses password encoder by default to encode the password and also enforces us to use the same.
To create our own user we need to provide password encoder. Password Encoder is an interface and we must provide implementation of it.
Spring enforces us to use BCryptPasswordEncoder which is the most famous password encoder at this time.

  1. Create a Configuration class and add below bean to it.
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder(10);
    }
  1. Then autowire this bean in ApplicationSecurityConfig and use it to encode the password like below.
    @Override
    @Bean
    protected UserDetailsService userDetailsService() {
        UserDetails mariumUser = User.builder()
                .username("student")
                .password(passwordEncoder.encode("student"))
                .roles("STUDENT")
                .build();

        return new InMemoryUserDetailsManager(mariumUser);
    }

Role Based Authentication
For Role based authentication, Follow these steps.

  1. Create an Enum ApplicationUserRoles.
public enum ApplicationUserRoles {
    STUDENT,
    ADMIN;
}  
  1. In ApplicationSecurityConfig, add below code to create two users with roles.
@Override
    @Bean
    protected UserDetailsService userDetailsService() {
        UserDetails studentUser = User.builder()
                .username("student")
                .password(passwordEncoder.encode("student"))
                .roles(STUDENT.name()) // ROLE_STUDENT
                .build();

        UserDetails adminUser = User.builder()
                .username("admin")
                .password(passwordEncoder.encode("admin"))
                .roles(ADMIN.name()) // ROLE_ADMIN
                .build();

        return new InMemoryUserDetailsManager(
                studentUser,
                adminUser
        );
    }
  1. Add below code in configure method to only allow users with role STUDENT to access the API.
 @Override
    protected void configure(HttpSecurity http) throws Exception {
        // any request should be authenticated i.e user should provide username and password and mechanism should be basic auth
        // antMatchers --> to allow access to home page like index to all users without any username and password
        // antMatchers("/api/**").hasRole(STUDENT.name()) --> Only allow user who have a role of STUDENT to access this API

        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/", "index", "/css/*", "/js/*").permitAll()
                .antMatchers("/api/**").hasRole(STUDENT.name())
                .anyRequest()
                .authenticated()
                .and()
                .httpBasic();
    }

Permission Based Authentication

  1. Add below method in ApplicationUserRoles ENUM.
public Set<SimpleGrantedAuthority> getGrantedAuthorities() {
        Set<SimpleGrantedAuthority> permissions = getPermissions().stream()
                .map(permission -> new SimpleGrantedAuthority(permission.getPermission()))
                .collect(Collectors.toSet());
        permissions.add(new SimpleGrantedAuthority("ROLE_" + this.name()));

        return permissions;
    }
  1. Add below code in UserDetailsService Bean in ApplicationSecurityConfig class.
@Override
    @Bean
    protected UserDetailsService userDetailsService() {
        UserDetails studentUser = User.builder()
                .username("student")
                .password(passwordEncoder.encode("student"))
//                .roles(STUDENT.name()) // ROLE_STUDENT
                .authorities(STUDENT.getGrantedAuthorities())
                .build();

        UserDetails adminUser = User.builder()
                .username("admin")
                .password(passwordEncoder.encode("admin"))
//                .roles(ADMIN.name()) // ROLE_ADMIN
                .authorities(ADMIN.getGrantedAuthorities())
                .build();

        UserDetails tomUser = User.builder()
                .username("tom")
                .password(passwordEncoder.encode("password123"))
//                .roles(ADMINTRAINEE.name()) // ROLE_ADMINTRAINEE
                .authorities(ADMINTRAINEE.getGrantedAuthorities())
                .build();

        return new InMemoryUserDetailsManager(
                studentUser,
                adminUser,
                tomUser
        );
    }
  1. Add below ant matchers in configure method.
@Override
    protected void configure(HttpSecurity http) throws Exception {
        // any request should be authenticated i.e user should provide username and password and mechanism should be basic auth
        // antMatchers --> to allow access to home page like index to all users without any username and password
        // antMatchers("/api/**").hasRole(STUDENT.name()) --> Only allow user who have a role of STUDENT to access this API

        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/", "index", "/css/*", "/js/*").permitAll()
                .antMatchers("/api/**").hasRole(STUDENT.name())
                .antMatchers(HttpMethod.DELETE,"/management/api/**").hasAuthority(COURSES_WRITE.getPermission())
                .antMatchers(HttpMethod.POST,"/management/api/**").hasAuthority(COURSES_WRITE.getPermission())
                .antMatchers(HttpMethod.PUT,"/management/api/**").hasAuthority(COURSES_WRITE.getPermission())
                .antMatchers(HttpMethod.GET,"/management/api/**").hasAnyRole(ADMIN.name(), ADMINTRAINEE.name(), STUDENT.name())
                .anyRequest()
                .authenticated()
                .and()
                .httpBasic();
    }

Permission Based Authentication With Annotations

  1. Add below annotation at ApplicationSecurityConfig Class.
@EnableGlobalMethodSecurity(prePostEnabled = true)
  1. Add below annotation on API resources.
@PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_ADMINTRAINEE')") // For role based for get all students
@PreAuthorize("hasAuthority('student:write')") // for permission based authentication for accessing write based resources like register student, delete student, update student etc