5 Core Concepts of Spring Security

  1. Authentication and Authorization
    – Authentication – Who are you – Answer by showing ID(Facebook, LinkedIn for ID which uniquely identifies you)
    – Authorization – What you want – State what you want

    Knowledge Based Authentication – Providing details you know about you to prove its you. Downside is details can be stolen.
    Possession Based Authentication – Key Cards for accessing Building Doors, Phone OTP. Authenticates by checking the user posses something which
    realuser should posses.

    Multi Factor Authentication – Enter password and enter OTP(KBA + PBA)

  2. Authorization – Checks whether the person is allowed to do something. For Authorization, Authentication is needed at first place.
  3. Principal
    – Person identified through process of Authentication
    – Person who has logged in. Currently logged in user (or) account.
    – App remembers the principal in context as currently loggedin user.
  4. Granted Authority
    – Authority includes whether the user is allowed to Read, Write, Update and Delete at permission level
  5. Role
    – Group of Authorities assigned together forms a role

Formbased Authentication
pom.xml

.
.
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>
.
.

Basic Auth
null

  1. Client Sends a request without username and password and gets 401 Unauthorized as Response
  2. Now Client Sends a request with username and password with Base64 Encoding
  3. Server validates whether user exists in DB
  4. Server replies with 200 Ok if user authentication is successful
  5. Basic ENCODED-BASE64-USERIDPASSWORD is the one sent in header to server from client
  6. In postman basic auth can be done by adding Authorization and base64 encoded user and password to header
    Header : Authorization
    Value : Basic base64('YourOrgName:YourAPIKEY');
    
  7. Base64 encoded text can be got from JS Console in browser as below

    "username:password!" // Here I used basic Auth string format
    
    // Encode the plain string to base64
    btoa("username:password!"); // output: "dXNlcm5hbWU6cGFzc3dvcmQh"
    
    
    // Decode the base64 to plain string
    atob("dXNlcm5hbWU6cGFzc3dvcmQh"); // output: "username:password!"
    
  8. Using Authorization Tab in post man does the same thing of adding base64 encoded UserName and Password to Header prepending Basic

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.

Form-based authentication
Form-based authentication is not formalized by any RFC.They don’t use the formal HTTP authentication techniques.They use the standard HTML form fields to pass the username and password values to the server.The server validates the credentials and then creates a “session” that is tied to a unique key that is passed between the client and server on each http put and get request.When the user clicks “log off” or the server logs the user off (for example after certain idle time), the server will invalidate the session key, which makes any subsequent communication between the client and server require re-validation

null

Basic Auth
null

Basic Auth with Authorization in Headers as seen in DevTool
null

Creating the below class in Spring Boot project would enable the Basic auth(httpAuth) instead of default formbased auth which we get after adding spring security starter dependency to pom.xml

ApplicationSecurityConfig.java
Using Custom Username and Password for Inmemory Authentication

@Configuration
@EnableWebSecurity
public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception{
        httpSecurity.authorizeRequests()
                    .anyRequest()
                    .authenticated()
                    .and()
                    .httpBasic();
    }
}

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";
    }
}
  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");
}
  1. Advantage of JSON token is it could be used form multiple platforms like PC, Mobile for accessing secured pages
  2. User enters user credentials(Username and Password) and gets authenticated.
  3. User Credentails are validated and token is generated
  4. On Subsequent request from APIs tokens are used in header for authorization
  5. Tokens are generated and verified by filters.There would be line of filters on way from request to API
  6. JwtUsernameAndPasswordAuthenticationFilter generates the Token after authentication
  7. JwtTokenVerifierFilter authorizes the user in each and every request by interpreting the token in the request header

JwtTokenVerifierFilter.java

public class JwtTokenVerifierFilter extends OncePerRequestFilter {
    private final SecretKey secretKey;
    private final JwtConfig jwtConfig;

    public JwtTokenVerifierFilter(SecretKey secretKey,
                                  JwtConfig jwtConfig) {
        this.secretKey = secretKey;
        this.jwtConfig = jwtConfig;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest,
                                    HttpServletResponse httpServletResponse,
                                    FilterChain filterChain) throws ServletException, IOException {

        String authorizationHeader = httpServletRequest.getHeader(jwtConfig.getAuthorizationHeader());


        if(Strings.isNullOrEmpty(authorizationHeader) || !authorizationHeader.startsWith("Bearer ")){
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }

        String token = authorizationHeader.replace(jwtConfig.getTokenPrefix(), "");

        try{
            Jws<Claims> claimsJws = Jwts.parser()
                    .setSigningKey(secretKey)
                    .parseClaimsJws(token);

            Claims body     = claimsJws.getBody();
            String username = body.getSubject();


            List<Map<String, String>> authorities = (List<Map<String, String>>)body.get("authorities");
            Set<SimpleGrantedAuthority> simpleGrantedAuth = authorities.stream()
                    .map(m -> new SimpleGrantedAuthority(m.get("authority")))
                    .collect(Collectors.toSet());

            Authentication authentication = new UsernamePasswordAuthenticationToken(username,  null, simpleGrantedAuth);
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }catch (JwtException e){
            throw new IllegalStateException(String.format("Token %s cannot be Trusted", token));
        }

        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }
}

JwtUsernameAndPasswordAuthenticationFilter.java

public class JwtUsernameAndPasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    private final AuthenticationManager authenticationManager;
    private final JwtConfig jwtConfig;
    private final SecretKey secretKey;


    public JwtUsernameAndPasswordAuthenticationFilter(AuthenticationManager authenticationManager,
                                                      JwtConfig jwtConfig,
                                                      SecretKey secretKey) {
        this.authenticationManager = authenticationManager;
        this.jwtConfig = jwtConfig;
        this.secretKey = secretKey;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response) throws AuthenticationException {

        try {
            UsernameAndPasswordAuthenticationRequest authenticationRequest = new ObjectMapper()
                    .readValue(request.getInputStream(), UsernameAndPasswordAuthenticationRequest.class);

            Authentication authentication = new UsernamePasswordAuthenticationToken(
                    authenticationRequest.getUsername(),
                    authenticationRequest.getPassword()
            );

            Authentication authenticate = authenticationManager.authenticate(authentication);
            return authenticate;

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain,
                                            Authentication authResult) throws IOException, ServletException {

        String token = Jwts.builder()
                .setSubject(authResult.getName())
                .claim("authorities", authResult.getAuthorities())
                .setIssuedAt(new Date())
                .setExpiration(java.sql.Date.valueOf(LocalDate.now().plusWeeks(jwtConfig.getTokenExpirationAfterDays())))
                .signWith(secretKey)
                .compact();

        response.addHeader(jwtConfig.getAuthorizationHeader(), jwtConfig.getTokenPrefix() + token);
    }
}

Download Code

Irrespective of Devops tool you use the below are the generic steps which would be carried out from
code changes to deployment in environment

Build Pipeline

  1. Step 1-Preparation of Environment for Build. This includes initializing buildno, commitid, buildname which would be used internally by the devops tool
  2. Step 2-Build Script for creating artifact
  3. Step 3-Copy Script for Copying Artifact to Staging Directory. Environment Directory(Directory on which new artifact would be created after build) to Staging Directory(Directory from which artifact would be created to drop location)
  4. Step 4-Publishing Script – to publish the artifact to DROP location. This is like common place which would be refered by release pipeline

Release Pipeline

  1. From DROP location the artifact would be picked. For this Trigger should be used.
  2. Trigger should detect the availability of new release and in which branch should be checked for new release.
  3. New artifact should be pushed to Deployment Environment(Dev, Testing or Prod). For this push Command should be used
  4. There should be agent to carry out the above tasks
  1. Wiremock Intro?
    • Serves canned responses to particular requests (stubbing), and that captures incoming requests so that they can be checked later (verification).
    • External to application code which helps in the testing app in Dev and ST(System Testing) environment
    • Helps in testing negative Scenarios like network latency
    • Language Agnostic and production like testing
  2. How to differentiate Positive and Negative Scenarios?
    It could be done based on method(GET, POST, DELETE, PUT), Path(/employee, /success) and based on Parameter.

     GET /employee?name=mugil
           
     Method - GET
     Path   - /employee
     Params - name       
    

  3. How priority of Stub Response is selected when Paths overlap?
    Stub is lowest no in priority would be selected.

    	//Priority 1
    	wireMockServer.stubFor(get("/welcome")
    			.atPriority(1)
    			.willReturn(aResponse()
    		        .withBody("Welcome with Priority 1")));
    
    	//Priority 2
            wireMockServer.stubFor(get("/welcome")
    		.atPriority(2)
                    .willReturn(aResponse()
                    .withBody("Welcome with Priority 2")));
    

    Output

    Welcome with Priority 1
    
  4. What is advantage of using Wiremock as Standalone?
    • Less dependency on Environment
    • Useful for Load Testing
    • Can be used by non JVM environment
    • Can run remotely
  5. What are possible defects which could happen in application?
    1. Timeout
    2. Server Error
    3. Invalid Response

Relationships between Classes

  1. Association
    • Composition
    • Aggregation
  2. Generalization
  3. Realization
  4. Dependency

Association(Has-A Relationship)

  1. Association is a relationship between two separate classes that establish through their objects. Each objects have their own life-cycle and there is no owner. Association can be one-to-one, one-to-many, many-to-one, many-to-many. It isn’t a “has-a” relationship and only means that the objects “know” each other

    class Bank  
    { 
        private String name;      
    
        Bank(String name){ 
            this.name = name; 
        } 
          
        public String getBankName(){ 
            return this.name; 
        } 
    }    
    
    class Employee 
    { 
        private String name; 
       
        Employee(String name){ 
            this.name = name; 
        } 
          
        public String getEmployeeName(){ 
            return this.name; 
        }  
    } 
      
    class Association  
    { 
        public static void main (String[] args)  
        { 
            Bank bank = new Bank("Axis"); 
            Employee emp = new Employee("Mani"); 
              
            System.out.println(emp.getEmployeeName() +  
                   " is employee of " + bank.getBankName()); 
        } 
    } 
    

    In above example two separate classes Bank and Employee are associated through their Objects. Bank can have many employees, So it is a one-to-many relationship.

  2. Aggregation and Composition are subsets of association meaning they are specific cases of association.Both of them conveys a has-a relationship to strong and weak degree
    1. Composition(Strong has-a relationship)
    2. Aggregation(Weak has-a relationship)
  3. has-a relationship simply means that an instance of one class has a reference to an instance of another class or an other instance of the same class
  4. Composition denotes a has-a relationship of Strong degree. Relationship among class members are strong.Classes which are grouped by Composition doesnot makes sense when interpreted seperately. I.E. when Person, Heart, Hand were seperated Heart and Hand doesnot make much sense.Objects in a composition relationship cannot, conceptually, exist in isolation. If we destroy the owner object, its members also will be destroyed with it

    public class WebPage { 
        private final PageHeader header;
        private final PageBody pageBody;
     
        public WebPage(PageHeader header, PageBody pageBody) {
            this.header = header;
            this.pageBody = pageBody;
        }
    }
     
    class PageHeader { 
        private String title;
        private String charset; 
    }
     
    class PageBody { 
        private String body;
    }
    
  5. Aggregation denotes a has-a relationship of Weak degree.Relationship among class members are weak. I.E. When trees were seperated from City Class Trees still make perfect sense and stand on it own.Objects on both sides of an aggregation relationship can exist in isolation.

    public class Team { 
        private List<Player> players = new ArrayList<>();
        public void addPlayer(Player player) {
            this.players.add(player);
            player.setTeam(this);
        }
     
        public void removePlayer(Player player) {
            this.players.remove(player);
            player.setTeam(null);
        }
    }
     
    class Player { 
        private String name;
        private int age;
     
        public void setTeam(Team team) {
            this.team = team;
        }
    }
    
  6. In UML notation, a composition is denoted by a filled diamond, while aggregation is denoted by an empty diamond, which shows their obvious difference in terms of strength of the relationship.

Composition vs Aggregation

Generalization(Is-A Relationship)

  1. In inheritance, a child of any parent can access, update, or inherit the functionality as specified inside the parent object. A child object can add its functionality to itself as well as inherit the structure and behavior of a parent object.This type of relationship collectively known as a generalization relationship.
  2. public class Vehicle 
    {    
    
    }
     
    class Truck extends Vehicle 
    {
    
    }
     
    class Boat extends Vehicle 
    {
     
    }
    

Realization(interfaces)

  1. In a realization relationship of UML, one entity denotes some responsibility which is not implemented by itself and the other entity that implements them. This relationship is mostly found in the case of interfaces.
  2. Realization is a specialized abstraction relationship between two sets of model elements, one representing a specification (the supplier) and the other represents an implementation of the latter (the client).

  3. class Hyndai implements Car {
    .
    .
    }
    

Dependency

  1. In a dependency relationship, as the name suggests, two or more elements are dependent on each other.Dependency relationships exist when classes depend on each other in such a way that a change to one class may affect the other, such as when one class accepts an instance of another class as parameter to a method.
  2. Dependency –> A references B (as a method parameter or return type)

    public class A {
        private C c;
        public void myMethod(B b) {
            b.callMethod();
        }
    }
    
  3. Dependency relationships are represented by arrows on dashed lines. Dependency represents the weakest of relationship
  4. A dependency relationship can exist when we have a Library class that manages Book objects. Since the Library class has a method that returns a Book, changes to the Book class could result in changes to the Library class (based on how Book objects are created).

    public class Library
    { 
        public Book findBook(String name) {
            //Do some book stuff.
            return new Book();
        }
    }
     
    class Book { 
        private String name;
        private String author;
    }
    
  5. A dependency typically (but not always) implies that an object accepts another object as a method parameter, instantiates, or uses another object. A dependency is very much implied by an association.
Posted in UML.

UML Diagrams

  1. Structure Diagrams
    • Class Diagrams
    • Deployment Diagrams
  2. Behaviour Diagrams
    • Use Case Diagrams
    • Activity Diagrams
    • State Machine Diagrams
  3. Interaction Diagrams
    • Sequence Diagrams

Class Diagrams Basics

  1. Class Contains ClassName, Attributes and Operations
  2. Class Names are written in Bold or Italic if class is Abstract
  3. Attributes

     <Visibility><Name>:<Type>=<Defaultvalue><{Modifier}>
    
         
      Visibility 
        + - Public
        - - Private 
        ~ - Package
        # - Protected 	
      Name   
        Name of Variable
      Type 
        Integer, String, Boolean
      DefaultValue 
        Value by DefaultValue
      Modifier
        ReadOnly
    
  4. Operations

     <Visibility><Name>(<ParameterList>):<ReturnType>	
    
      Visibility 
        + - Public
        - - Private 
        ~ - Package
        # - Protected 	
      Name   
        Name of Operations
      ParameterList		
        List of Parameters - Comma Seperarted or Empty
      ReturnType
        Integer, String or Another Class  
    
  5. Static Elements – Static Operations and Attributes are underlined in UML Diagram

Relationships

Posted in UML.