My Introduction to Spring Security
This week at work, I've been working on migrating our application to containers to shift to the cloud.
We decided a good point to start would be with the Access Control application which takes care of the authentication and authorization of users to our application. Since we're a Java/Spring Boot shop, this was built upon Spring Security which led to down the rabbit hole of investigating/learning how Spring Security works and this blog page summarizes my learning.
A lot of the information in this book has been taken from [1] and I highly recommend the resource to learn in-detail about Spring Security!
Introduction
Before we dive into Spring Security, we need to understand the difference between Authentication and Authorization.
Authentication is proving who you say you are; the most common way this is done is with a username and a password.
Authorization on the other hand is about what services/pages you have access to following the authentication process, this is usually handled with roles assigned to users/groups.
Adding Spring Security
If we're using Spring Boot, we have a lot of auto configuration that's done for us and thus, just adding Spring Security dependency [3] gives us a good starting point.
If we just have the above dependency and an endpoint defined as below:
Once we start the application; we will see a password in the console that needs to be used to access the endpoint.
Behind the scenes, cURL encodes the string <username>:<password> in Base64 and sends it as the value of the Authorization header prefixed with the string Basic.
Just by adding the dependency above, with the default configuration, we get 2 different authentication mechanisms:
- HTTP Basic
- Form Login
If we access the endpoint 'localhost:8080/hello' from the web browser, we will see the Form Login.
The image below shows an overview of the main components of Spring Security, it's been taken from [1].
InMemoryUserDetailsManager
InMemoryUserDetailsManager [4] implements the UserDetailsService to provide support for username/password based authentication that is stored in-memory.
PasswordEncoder
For the above listing, we also need to define a Password Encoder.
NoOpPasswordEncoder doesn't encrypt or hash passwords so not recommended to use in anything beyond development/proof of concepts.
Customizing Authentication and Authorization
SecurityFilterChain
To customize authentication and authorization, we need to define a bean of type 'SecurityFilterChain'.
The above code allows responses to only authenticated requests.
In the code above, the 'http.httpBasic()' added the HTTP Basic authentication which we've been using in our curl commands.
The other method 'http.authorizeHttpRequests()' configures the authorization rules at the endpoint level.
Both the above methods used the 'Customizer' functional interface[5].
Another configuration process
In the example above, we created a CustomConfiguration object in which we have 2 methods that returned an UserDetailsService object and a PasswordEncoder object, we can setup the configuration to directly use the SecurityFilterChain to set both.
Defining custom authentication logic
We can define custom logic by implementing the 'AuthenticationProvider' and then provide this in the configuration class by injecting the 'CustomAuthenticationProvider' into the ProjectConfig class and passing it to the 'http.authenticationProvider(authenticationProvider)' method.
Separating Configurations
It is a good practice to separate the responsibilities for the configuration classes as shown below:
Summary
This blog post is already getting too long and I haven't even gotten to the interesting parts (implementing OAuth/customize authorization with filters) yet which I'll post about coming soon!
Resources:
[2] Spring Docs
[4] https://docs.spring.io/spring-security/reference/servlet/authentication/passwords/in-memory.html
[5] https://docs.spring.io/spring-security/reference/api/java/org/springframework/security/config/Customizer.html
[6]
Comments
Post a Comment