This readMe file is created along with the journey of learning the Spring Security concepts using free course available on JavaBrains youtube course
Spring-security concepts
- Encoding -> Input can be derived from the output. Anyone can guess and read the original message.
- Encryption -> Input can be derived only by secret key. Anyone with a secret key can read the original message.
- Hashing -> Input can never be derived by any means. You can only convert input into the same hash and see if it is tampored or not.
Security pillers | Description |
---|---|
Authentication | Who are you ? This can be knowledge base or posession based. |
Authorization | What do you want ? Are you authorized to access this ? |
Principal | Once you logged in to the system, "principal" will store credentials of user. |
Granted Authority | What they can do i.e. place_order/do_checkOut. Authority to perform certain operation. |
Roles | Basically, a group of authority so that it can be well managed and easily assignable to anyone. |
spring-boot-starter-security - it is responsible to provide all APIs related to spring security in our application.
Once you will add this maven dependency, you will get
- Input form for username/password
- Validation for the same
- If you have not provided any username/passoword then Spring will generate one password for user "User" everytime you will start the spring application.
- Username & password can be configured via providing following properties in application.properties file OR application.yml file :
spring.security.user.name=<userName> spring.security.user.password=<password>
Authentication manager manages authentication.
authenticate()
Authenticate() method perform authentication on uesr and return if user is successfully authenticated or throws exception.
This is a builder pattern. You should use it to configure authentication for your application. It is a 2 steps process :
- Get hold of AuthenticationManagerBuilder
- Set the configuaration on it.
configure(AuthenticationManagerBuilder)
When you extend this class, spring framework gives you opportunity to override configure() method where you will get instance of AuthenticationManagerBuilder, You can now modify it and pass your spring security configuration as per the requirement. It is 2 step process to configure builder instance :
- Tell which type of authetication you want i.e. inMemoryAuthentication()
- Pass user, password & role.
Example :
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("hiren")
.password("hiren")
.roles("User");
}
/**
* Clear text password is strict NO in Spring.
* Just for tutorial purpose we are passing this.
* @return
*/
@Bean
public PasswordEncoder getPasswordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
- We will be configuring who will access WHAT !!
- One user would be having one specific role and the other will be having the other specific role.
- We will use again the same method we used to configure authentication which is configure() method.
configure(httpSecurity)
httpSecurity can be used to configure authorization here.
Following will be our user-role mapping :
API end point | Roles allows to access it |
---|---|
/ | All (unauthenticated) |
/user | USER/ADMIN roles |
/admin | ADMIN role |
http.authorizeRequests()
.antMatchers("/**").hasRole("ADMIN")
.and().formLogin();
In above code snippet :
- "/**" means any URL and any nested URL.
- hasRole("ADMIN") means all URLs (in this case) are only accessible by users who has ADMIN role.
Below code snippet is the more advance version :
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin").hasRole("ADMIN")
.antMatchers("/user").hasAnyRole("ADMIN", "USER")
.antMatchers("/").permitAll()
.and().formLogin();
}
Here,
- Higher & more restrictive role must come at the first place and then less restrictive.
- If you will not follow the point number 1 then Spring security may not behave as per your configuration.
When you add spring security dependency, Spring will automatically start intercepting all the incoming requests using DelegatingFilterProxy before delegating it to servlets. So there are two specific filters which performs authentication and authorization.
When Spring start authentication it takes credentials as input and returns principal as output. Object type will be authentication.
Here is the method signature from interface AuthenticationProvider
Authentication authenticate(Authentication authentication)
- First filter will intercept the incoming request
- Then it will pass the credential to the AuthenticationManager
- Authentication manager then identify the implementation.
- Based on AuthenticationProvider it will use credential storage resources to authenticate the the user and returns the Principal object in the form of Authentication object.
- It will be then stored in the ThreadLocal object to extract user related information like username, password, isAuthenticatd boolean etc.
So after adding following dependency for H2 database we are now able to run the application using "schema.sql" & "data.sql"
- We have added 2 users into "users" table
- We have added 2 authorities named "USER" & "ADMIN"
- We have created schema having two tables "users" & "authorities"
- Above tables are already known to Spring so we do not have to write extra code or to do extra configuration.
We have added following dependencies to make it up and running with H2 database.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
usersByUserNameQuery authoritiesByUserNameQuery Above two builder pattern methods will allow you to handle this scenario. Sample Code :
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("select username, password, enabled from users where username = ?")
.authoritiesByUsernameQuery("select username, authority from authorities where username = ?");