1. CSRF Refers to Cross Site Request Forgery. More on CSRF Here
  2. When the Client makes the first GET request to the Server, Server generates CSRF token and sends back to Client
  3. On the Subsequent PUT,POST and DELETE request from the Client this token would be used for Authentication
  4. In Postman for the Same reason GET request would work irrespect we disable the CSRF in protected void configure(HttpSecurity httpSecurity) method. But for the PUT,POST and DELETE we should disable the CSRF as below otherwise the server would expect CSRF token when the request to server is PUT,POST and DELETE.
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception
    {
    
    httpSecurity.csrf().disable()
                .authorizeRequests()
                .antMatchers("/", "index", "/css/*", "/js/*").permitAll()
                .
                . 
    

Using CSRF Token for Authorization

  1. We have set of API’s exposed to the users whose role is ADMIN
  2. The way CSRF works is first Token would be generated once the User Logins using UserID and Password. The Way it works in REST API is first time
    when you use GET method you should use Basic Auth in Authorization to Generate XSRF-TOKEN which would be set in Cookie along with JSessionID and also available in Response Headers
  3. By default CSRF is not applicable for GET unless it is pointed to resource API. Simple reason behind that is, when the user types URL of login thats the first page which take login. If you try to access http://localhost:8080/ in postman it works fine without CSRF but when you access http://localhost:8080/api/v1/students/ which again uses GET method but points to resource API then it asks for UserId and Password due to basic Auth
  4. Subsequent POST, PUT and DELETE request could be done by attaching X-XSRF-TOKEN to header. The Cookie which has the JSESSIONID and XSRF-TOKEN should not be deleted in Postman
  5. Though CSRF is enabled explicity in my Code it didnt worked until i added following lines in ApplicationSecurityConfig.java
    .
    .
            httpSecurity
                         .csrf()
                         .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
    .
    

Accessing resource using GET (No Authentication or Authorization)

Accessing resource using GET (Authentication needed for API Resource)

Accessing resource using POST (X-XSRF-TOKEN) in request header

XSRF-TOKEN received after GET with credentials and X-XSRF-TOKEN in haeder for sunsequent POST,PUT and DELETE calls

ApplicationSecurityConfig.java
Using CSRF Token for Authorization

@Override
    protected void configure(HttpSecurity httpSecurity) throws Exception{
        httpSecurity
                     .csrf()
                     .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                     .and()
                    .authorizeRequests()
                    .antMatchers("/", "index", "/css/*", "/js/*").permitAll()
                    .antMatchers("/api/**").hasRole("ADMIN")
                    .anyRequest()
                    .authenticated()
                    .and()
                    .httpBasic();
}

FAQ
There are N number of possibilities. Try out in Postman.

  1. How GET method to API resource Works with out CSRF token?
    It works based on JSESSIONID. When the JSESSIONID in cookie is deleted then again it asks for Login and password
  2. What happens when I delete XSRF token in Cookie?
    New Token would be generated based on JSESSIONID in cookie
  3. What happens when I delete XSRF token and in Cookie and try POST, DELETE and PUT over API?
    New JSESSIONID would be generated and placed in cookie. For this X-XSRF-TOKEN should be passed in header.
  1. What is the difference between FormBased and BasicAuth?
    The Difference between FormAuth and BasicAuth is in BasicAuth UserName and Password would be sent everytime when making a request to the server in the header as base64 encoded character. BasicAuth happens at http Protocol level where as Formbased Auth happens at Framework level.
  2. What is the difference between JKS and PKCS12?
    JKS is the most common if you stay within the Java world. PKCS#12 isn’t Java-specific, it’s particularly convenient to use certificates (with private keys) backed up from a browser or coming from OpenSSL-based tools.JKS, Java Key Store. You can find this file at sun.security.provider.JavaKeyStore. This keystore is Java specific, it usually has an extension of jks. This type of keystore can contain private keys and certificates, but it cannot be used to store secret keys. Since it’s a Java specific keystore, so it cannot be used in other programming languages.PKCS12, this is a standard keystore type which can be used in Java and other languages. You can find this keystore implementation at sun.security.pkcs12.PKCS12KeyStore. It usually has an extension of p12 or pfx. You can store private keys, secret keys and certificates on this type.

  3. How safe is Userid and Password in Basic Auth?
    UserId and Password in BasicAuth would transferred using base64 encoded UserName and Password attached to Header prepending Basic

    Authorization : Basic base64(UserId:Password)
    
  4. How POST, PUT and DELETE works in Basic Auth?
    In Basic Auth with respect to Spring Security CSRF Token would be used incase CSRF is enabled. If CSRF is disabled Authorization in header would be used
    for access. csrf().disable() should be always used incase you are using other than GET method.
  5. What is JSESSIONID?
    JSESSIONID which is generated and Stored as Cookie in Postman helps the spring boot app to recognizane whether the User is Authenticated or not.
  6. How Spring Security handles CSRF in client Side when enabled?
    Spring Security generates JSESSIONID and XSRF-TOKEN. Both were Set in Cookie. On Subsequent request X-XSRF-TOKEN should be sent in Header for Authorization.
  7. What happens when I delete XSRF token in Cookie?
    New Token would be generated based on JSESSIONID in cookie
  8. What happens when I delete XSRF token and in Cookie and try POST, DELETE and PUT over API?
    New JSESSIONID would be generated and placed in cookie. For this X-XSRF-TOKEN should be passed in header.
  9. Where is JSESSION stored?
    JSESSION is session ID which is stored in Inmemory Database in Server and in a Cookie in Client Side.
  10. What is the default Expiration time of JSESSION Cookie?
    30 Minutes
  11. Spring Security Arcitecture?
  12. Encoding vs Encrytion vs Hashing?
    Encoding refers to any transformation of a given input. For example, if we have a function x that reverses a string, function x -> y applied to ABCD produces DCBA.

    x -> y
    

    Encryption is a particular type of encoding where, to obtain the output, you provide both the input value and a key. The key makes it possible for choosing afterward who should be able to reverse the function (obtain the input from the output). The simplest form of representing encryption as a function looks like this:

    (x, k) -> y
    

    where x is the input, k is the key, and y is the result of the encryption. This way, an individual knows the key can use a known function to obtain the input from the output (y, k) -> x. We call this reverse function decryption. If the key used for encryption is the same as the one used for decryption, we usually call it a symmetric key.If we have two different keys for encryption ((x, k1) -> y) and decryption ((y,k2) -> x), then we say that the encryption is done with asymmetric keys. Then (k1,k2) is called a key pair. The key used for encryption, k1, is also referred to as the public key, while k2 is known as the private one. This way, only the owner of the private key can decrypt the data.

    Hashing is a particular type of encoding, except the function is only one way. That is,from an output y of the hashing function, you cannot get back the input x. However,there should always be a way to check if an output y corresponds to an input x, so we can understand the hashing as a pair of functions for encoding and matching. If hashing is x -> y, then we should also have a matching function (x,y) ->boolean.

    (x,y) ->boolean
    

    Sometimes the hashing function could also use a random value added to the input:
    (x, k) -> y. We refer to this value as salt. The salt makes the function stronger, enforcing the difficulty of applying a reverse function to obtain the input from the
    result.

  13. Two ways of Configuring Spring Security?
    Method 1: Adding userservice and passwordEncoder in configure method(Not Recommended)

    @Configuration
    public class ProjectConfig extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.inMemoryAuthentication()
                    .withUser("john")
                    .password("12345")
                    .authorities("read")
                    .and()
                    .passwordEncoder(NoOpPasswordEncoder.getInstance());
        }
    }
    

    Method 2: Using Seperate Configuration class for Bean and injecting beans(Recommended Method)
    PasswordConfig.java

    @Configuration
    public class PasswordConfig {
        @Bean
        public PasswordEncoder passwordEncoder()
        {
            return new BCryptPasswordEncoder(10);
        }
    }
    

    ApplicationSecurityConfig.java

    @Configuration
    @EnableWebSecurity
    public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter {
       @Autowired
       private 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);
        }
    
     
        @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();
        }
    }
    
  14. What are the interface methods available in Spring Security?
    UserDetailsManager
    public interface UserDetailsManager extends UserDetailsService {
    void createUser(UserDetails user);
    void updateUser(UserDetails user);
    void deleteUser(String username);
    void changePassword(String oldPassword, String newPassword);
    boolean userExists(String username);
    }
    UserDetailsService
    public interface UserDetailsService {
    UserDetails loadUserByUsername(String username)
    throws UsernameNotFoundException;
    }
    UserDetails
    public interface UserDetails extends Serializable {
    String getUsername();
    String getPassword();
    Collection<? extends GrantedAuthority>
    ➥getAuthorities();
    boolean isAccountNonExpired();
    boolean isAccountNonLocked();
    boolean isCredentialsNonExpired();
    boolean isEnabled();
    }
    AuthenticationProvider
    public interface AuthenticationProvider {
    Authentication authenticate(Authentication authentication)
    throws AuthenticationException;
    boolean supports(Class<?> authentication);
    }
    Authentication
    public interface Authentication extends Principal, Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();
    Object getCredentials();
    Object getDetails();
    Object getPrincipal();
    boolean isAuthenticated();
    void setAuthenticated(boolean isAuthenticated)
    throws IllegalArgumentException;
    }


Note:The example is for CSRF disabled

  1. From the above diagram you can see JSESSIONID and HttpStatus.OK is send is response once the credentials are authenticated
  2. JSESSIONID would be used for Subsequent request
  3. In the below code we define URL for login and logout.
    .formLogin()
    .loginPage("/login").permitAll().usernameParameter("username").passwordParameter("password")
    .defaultSuccessUrl("/test", true)
    .and()
    .rememberMe()
    .and()
    .logout().logoutUrl("/logout").clearAuthentication(true).invalidateHttpSession(true).deleteCookies("JSESSIONID", "remember-me")
    .logoutSuccessUrl("/login")			
    
  4. usernameParameter and passwordParameter is the name of the input form element as given in html
  5. defaultSuccessUrl tells the default page after authentication
  6. rememberMe allows the User to remember the session in server. The default JSESSIONID time is 30 minutes of inactivity. remember-me session would be active for 2 weeks and allows user to access page for 2 weeks
  7. logout is similar to login with following
    .logout().logoutUrl("/logout")
    .clearAuthentication(true)
    .invalidateHttpSession(true)
    .deleteCookies("JSESSIONID", "remember-me")
    .logoutSuccessUrl("/login")			
    

JSESSIONID and remember-me as seen in cookie in response after login button clicked

JSESSIONID and remember-me cookie deleted in response after logout button clicked

login.html

<body>
<div class="container">
    <form class="form-signin" method="post" action="/login">
        <table cellpadding="3" cellspacing="3" border="1px solid black" style="border-collapse: collapse">
            <tr>
                <td><label for="username" class="sr-only">Username</label></td>
                <td><input type="text" id="username" name="username" class="form-control" placeholder="Username" required=""
                           autofocus=""></td>
            </tr>
            <tr>
                <td><label for="password" class="sr-only">Password</label></td>
                <td><input type="password" id="password" name="password" class="form-control" placeholder="Password"
                           required=""></td>
            </tr>
            <tr>
                <td><label for="remember-me" class="sr-only">Remember Me?</label></td>
                <td><input type="checkbox" id="remember-me" name="remember-me" class="form-control"></td>
            </tr>
            <tr>
                <td colspan="2" align="center"><button class="btn btn-lg btn-primary btn-block" type="submit">Login</button></td>
            </tr>
        </table>
    </form>
</div>
</body>

test.html

You have been Logged In

<form class="form-signin" method="get" action="/logout">
    <button class="btn btn-lg btn-primary btn-block" type="submit">Logout</button>
</form>
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception{
	httpSecurity
				.csrf().disable()
				.authorizeRequests()
				.antMatchers("/", "index", "/css/*", "/js/*").permitAll()
				.antMatchers("/api/**").hasRole("ADMIN")
				.anyRequest()
				.authenticated()
				.and()
				.formLogin()
				.loginPage("/login").permitAll().usernameParameter("username").passwordParameter("password")
				.defaultSuccessUrl("/test", true)
				.and()
				.rememberMe()
				.and()
				.logout().logoutUrl("/logout").clearAuthentication(true).invalidateHttpSession(true).deleteCookies("JSESSIONID", "remember-me")
				.logoutSuccessUrl("/login");
}