Let's create a new project from Spring Initializr https://start.spring.io/.
Let's build some API's.
We will create a new package com.example.springsecuritydemo.student and create a new class Student and StudentController inside it.
Inside the StudentController, we will add an API to fetch the student from the hard-coded list:
Start the application, and you can directly access the API.
Form Based Authentication
Now we will add Spring security so that we can do the authentication and authorization.
We need to add 'spring-boot-starter-security' dependency in our pom.xml.
Now if you restart the application and try to access the API by opening http://localhost:8080/api/v1/students/1 in browser, you will get a login page (form based authentication.
To access the API you need to enter the credentials. From where can we get the credentials? The username is 'user' and we can get the password from the console.
Basic Authentication With Spring Security
Instead of Spring's default form based authentication we will implement authentication with customized user name and password.
Let's create a new package com.example.springsecuritydemo.security and create a class ApplicationSecurityConfig inside it. We will annotate this class with @Configuration and @EnableWebSecurity.
@Configuration annotation helps in Spring annotation based configuration. This annotation indicates that a class declares one or more @Bean methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime. e.g:
@Configuration
public class MyConfiguration {
@Bean
public MyBean myBean() {
return new MyBean();
}
}
What if we remove @Configuration annotation? If we remove the @Configuration annotation from MyConfiguration class, it still works as expected and spring beans are registered and retrieved as singleton classes. But in this case, if we make a call to myBean() method then it will be a plain java method call and we will get a new instance of MyBean and it won’t remain singleton.
@EnableWebSecurity is a marker annotation. It allows Spring to find it's a @Configuration and, therefore, @Component and automatically apply the class to the global WebSecurity. If I don't annotate any of my class with @EnableWebSecurity still the application prompting for username and password because it is the default behavior. Apparently, if you add @EnableWebSecurity annotation on your class it will switch off the default web application security configuration and add your own.
The @EnableWebSecurity annotation is crucial if we disable the default security configuration.
If missing, the application will fail to start. The annotation is only optional if we're just overriding the default behavior using a WebSecurityConfigurerAdapter.
We created a class ApplicationSecurityConfig inside com.example.springsecuritydemo.security and annotated with @Configuration and @EnableWebSecurity. Now we need to extend WebSecurityConfigurerAdapter.
In Spring Boot 2, if we want our own security configuration, we can simply add a custom WebSecurityConfigurerAdapter. This will disable the default auto-configuration and enable our custom security configuration.
We will override configure() method which takes HttpSecurity as an argument. "http.authorizeRequests().anyRequest().authenticated().and().httpBasic()", this will ensures that any request to our application requires the user to be authenticated with HTTP Basic authentication.
Now restart the application, you will notice 'Using generated security password' is printed on the console. Try to access the API http://localhost:8080/api/v1/students/1 from the browser.
Earlier we had form based authentication, but now when you access the API you will receive a pop-up where you can enter the user credentials (username: user, get password from console) to access the API. This is basic auth.
There is a drawback of this approach, after entering the credentials you won't be able to logout. If you access http://localhost:8080/logout you will get an error page.
ANT Matchers For Spring Authentication
Let's say you want to white-list some URL's. To do this let's create an index.html inside src\main\resources\static folder. Since its the index page, whenever anyone opens http://localhost:8080/ this page will be shown.
With basic authentication, you need to enter user credentials even to access this index page. Now we want to white-list this page.
We need to make changes in our ApplicationSecurityConfig to white-list particular URL's. In our overridden config() method we need to add antMatchers("/", "index", "/css/*", "/js/*").permitAll() this will white-list all the css, javascripts and file with name index.
If you restart the application and try to access http://localhost:8080/, you won't need to enter user credentials.
Create In Memory User Details Manager for Spring Authentication
Let's create a class for Password encoding. Inside the com.example.springsecuritydemo.security create PasswordConfig, annotate it with @Configuration. Then we need to declare a method passwordEncoder() inside this class, which will return BCryptPasswordEncoder with strength as 10. We need to annotate passwordEncoder with @Bean.
We will Override userDetailsService() method in ApplicationSecurityConfig. Inside this method we will declare one user with name 'stu1', password as 'password' (password is encoded) and role as 'STUDENT' (internally role will be saved as ROLE_STUDENT).
We also need to declare PasswordEncoder in ApplicationSecurityConfig and add a constructor which takes PasswordEncoder.
After restarting the application, we can access http://localhost:8080/api/v1/students/1 by using username as 'stu1' and password as 'password'.
Defining Roles
Let's say we have two roles:
Then we will declare two ENUMS ApplicationUserPermission and ApplicationUserRole.
Now let's create CourseController and modify StudentController. I have added getStudent() and setStudent() API's in StudentController. Similar API's for courses are there in CourseController
After this we need to modify configure() method in ApplicationSecurityConfig. I have added antMatchers() for both Student and Courses API , to set the role we need to add hasRole().
Restart the application and try to access student API with admin credentials and courses API with student credentials, you will get an error. Student API can be accessed by student credentials, Courses API can be accessed by admin credentials.
We have two roles, we will add permissions:
We have not made any changes in StudentController and CourseController.
Now we need to make changes in ApplicationSecurityConfig. First of all instead of roles, we need to configure authorities in userDetailsService(). Then we need to add antMatchers in configure, which will allow only the users with COURSES_WRITE permission to call PUT, POST or DELETE on CourseController's API.
Restart the application and access the API's. Below API's can be called by both Student and Admin users
- Project: Maven Project
- Language: Java
- Spring Boot: 2.2.4
- Group: com.example
- Artifact: spring-security-demo
- Java Version: 8
- Dependencies: Spring Web
Let's build some API's.
We will create a new package com.example.springsecuritydemo.student and create a new class Student and StudentController inside it.
Inside the StudentController, we will add an API to fetch the student from the hard-coded list:
Start the application, and you can directly access the API.
Form Based Authentication
Now we will add Spring security so that we can do the authentication and authorization.
We need to add 'spring-boot-starter-security' dependency in our pom.xml.
Now if you restart the application and try to access the API by opening http://localhost:8080/api/v1/students/1 in browser, you will get a login page (form based authentication.
To access the API you need to enter the credentials. From where can we get the credentials? The username is 'user' and we can get the password from the console.
Basic Authentication With Spring Security
Instead of Spring's default form based authentication we will implement authentication with customized user name and password.
Let's create a new package com.example.springsecuritydemo.security and create a class ApplicationSecurityConfig inside it. We will annotate this class with @Configuration and @EnableWebSecurity.
@Configuration annotation helps in Spring annotation based configuration. This annotation indicates that a class declares one or more @Bean methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime. e.g:
@Configuration
public class MyConfiguration {
@Bean
public MyBean myBean() {
return new MyBean();
}
}
What if we remove @Configuration annotation? If we remove the @Configuration annotation from MyConfiguration class, it still works as expected and spring beans are registered and retrieved as singleton classes. But in this case, if we make a call to myBean() method then it will be a plain java method call and we will get a new instance of MyBean and it won’t remain singleton.
@EnableWebSecurity is a marker annotation. It allows Spring to find it's a @Configuration and, therefore, @Component and automatically apply the class to the global WebSecurity. If I don't annotate any of my class with @EnableWebSecurity still the application prompting for username and password because it is the default behavior. Apparently, if you add @EnableWebSecurity annotation on your class it will switch off the default web application security configuration and add your own.
The @EnableWebSecurity annotation is crucial if we disable the default security configuration.
If missing, the application will fail to start. The annotation is only optional if we're just overriding the default behavior using a WebSecurityConfigurerAdapter.
We created a class ApplicationSecurityConfig inside com.example.springsecuritydemo.security and annotated with @Configuration and @EnableWebSecurity. Now we need to extend WebSecurityConfigurerAdapter.
In Spring Boot 2, if we want our own security configuration, we can simply add a custom WebSecurityConfigurerAdapter. This will disable the default auto-configuration and enable our custom security configuration.
We will override configure() method which takes HttpSecurity as an argument. "http.authorizeRequests().anyRequest().authenticated().and().httpBasic()", this will ensures that any request to our application requires the user to be authenticated with HTTP Basic authentication.
Now restart the application, you will notice 'Using generated security password' is printed on the console. Try to access the API http://localhost:8080/api/v1/students/1 from the browser.
Earlier we had form based authentication, but now when you access the API you will receive a pop-up where you can enter the user credentials (username: user, get password from console) to access the API. This is basic auth.
There is a drawback of this approach, after entering the credentials you won't be able to logout. If you access http://localhost:8080/logout you will get an error page.
ANT Matchers For Spring Authentication
Let's say you want to white-list some URL's. To do this let's create an index.html inside src\main\resources\static folder. Since its the index page, whenever anyone opens http://localhost:8080/ this page will be shown.
With basic authentication, you need to enter user credentials even to access this index page. Now we want to white-list this page.
We need to make changes in our ApplicationSecurityConfig to white-list particular URL's. In our overridden config() method we need to add antMatchers("/", "index", "/css/*", "/js/*").permitAll() this will white-list all the css, javascripts and file with name index.
If you restart the application and try to access http://localhost:8080/, you won't need to enter user credentials.
Create In Memory User Details Manager for Spring Authentication
Let's create a class for Password encoding. Inside the com.example.springsecuritydemo.security create PasswordConfig, annotate it with @Configuration. Then we need to declare a method passwordEncoder() inside this class, which will return BCryptPasswordEncoder with strength as 10. We need to annotate passwordEncoder with @Bean.
We will Override userDetailsService() method in ApplicationSecurityConfig. Inside this method we will declare one user with name 'stu1', password as 'password' (password is encoded) and role as 'STUDENT' (internally role will be saved as ROLE_STUDENT).
We also need to declare PasswordEncoder in ApplicationSecurityConfig and add a constructor which takes PasswordEncoder.
After restarting the application, we can access http://localhost:8080/api/v1/students/1 by using username as 'stu1' and password as 'password'.
Defining Roles
Let's say we have two roles:
- 'ADMIN': Can access Courses API's.
- 'STUDENT': Can access Student API's.
Then we will declare two ENUMS ApplicationUserPermission and ApplicationUserRole.
After this we need to modify configure() method in ApplicationSecurityConfig. I have added antMatchers() for both Student and Courses API , to set the role we need to add hasRole().
Restart the application and try to access student API with admin credentials and courses API with student credentials, you will get an error. Student API can be accessed by student credentials, Courses API can be accessed by admin credentials.
- http://localhost:8080/api/v1/students/student, can be accessed by user name: student, password: password
- http://localhost:8080/api/v1/courses/course, can be accessed by user name: admin, password: password81
We have two roles, we will add permissions:
- 'ADMIN': An Admin can read/write Courses and Student.
- 'STUDENT': A Student can read/write Student and read Courses.
For this we need to modify ApplicationUserRole and give permission to Students to read the Courses. I have added a method getGrantedAuthorities() inside ApplicationUserRole which will return permissions and the role (we need to append ROLE_ with the user role).
Now we need to make changes in ApplicationSecurityConfig. First of all instead of roles, we need to configure authorities in userDetailsService(). Then we need to add antMatchers in configure, which will allow only the users with COURSES_WRITE permission to call PUT, POST or DELETE on CourseController's API.
- GET: http://localhost:8080/api/v1/students/student
- POST: http://localhost:8080/api/v1/students/student/1
- GET: http://localhost:8080/api/v1/courses/course
- POST: http://localhost:8080/api/v1/courses/course/1
No comments:
Post a Comment