Session 9.3: Spring Boot Security - Auth-service (JWT)
1. Clone the following repository
$ git clone https://github.com/M-Gharib/ESI-W9.3.git
If you want to create a new Spring Boot project from scratch, you need to install the following dependencies for both the Product and Inventory services:
- Spring Web
- Spring Security
- Spring Data JPA SQL
- PostgresSQL Driver SQL;
- Lombok
Also, you need to add the following dependency manually into your Pom.xml
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.5</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.5</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> artifactId>jjwt-jackson</artifactId> <version>0.11.5</version> </dependency>
The application has the following structure.
auth-service-jwt └── config └── MyUserDetails.java └── MyUserDetailsService.java └── SecurityConfig.java └── jwt └── JwtAuthFilter.java └── JwtService.java └── users └── controller └── userController.java └── dto └── UserDto.java └── model └── User.java └── repository └── userRepository.java └── service └── userService.java
The code is fully annotated to facilitate your comprehension. With respect to the previous week's project:
MyUserDetails.java
,MyUserDetailsService.java
,User.java
,userRepository.java
, anduserService.java
are the same, i.e., have not been modified.SecurityConfig.java
anduserController.java
have been modified.JwtAuthFilter.java
,JwtService.java
, andUserDto.java
are new.
2. Check the new/modified code in SecurityConfig.java
- The
securityFilterChain( .. )
function has been modified to enable the session manager to control the session, which prevents the request from being saved in the session (.STATELESS). Then, we are specifying our authenticationProvider as an authentication provider. Finally, we are telling Spring Boot to check ourauthFilter
(Jwt auth filter) first, then, checking theUsernamePasswordAuthenticationFilter
. - The
corsConfigurationSource( .. )
function (Bean)can be helpful in solving the Cors issue.
3. Check the new code in userController.java
, we have a new endpoint for authenticating a user @PostMapping("/authenticate")
. It takes the UserDto
as input, and it passes the username and password to the UsernamePasswordAuthenticationToken
to check whether the user can be authenticated. If the user is authenticated we generate the token, otherwise, we throw an exception.
4. Check the code in JwtAuthFilter.java
, it overrides the doFilterInternal(..)
function, which is a filter that is called each time a request/response pair is passed through the chain [of filters] due to a client request for a resource at the end of the chain. In short, the doFilterInternal(..)
function extracts the header of the "auth" request, the token, then extracts the user name from the token. Then, it checked if the user can be authenticated (the token contains a username) and also the user does not have any authority (not authenticated yet). After that, it tries to validate the token.
5. Check the code in JwtService.java
, there are functions for creating/generating the token, and pay particular attention to the claims, the secret, and the signing key. There are also functions for extracting different information from the token, and a function for validating the token, which checks whether the token has not expired yet.
Test our application
6. Run your application.
7. create an admin and user users, in the RestClientFile.rest
, there are two HTTP requests, which can be used to create both of these users.
### Create a new user with Role/Authority ADMIN POST http://localhost:8090/api/auth/signup HTTP/1.1 content-type: application/json { "name": "admin", "email": "admin@ut.ee", "password": "admin", "roles": "ADMIN" } ### Create a new user with Role/Authority USER POST http://localhost:8090/api/new HTTP/1.1 content-type: application/json { "name": "user", "email": "admin@ut.ee", "password": "user", "roles": "USER" }
8. Try to authenticate the admin user by sending the following request in the RestClientFile.rest
, the token with be send within the response for this request.
### Authenticate POST http://localhost:8090/api/auth/authenticate HTTP/1.1 content-type: application/json { "name": "admin", "password": "admin" }
9. Copy the returned token, visit https://jwt.io/, and paste the token within the Encoded part. Check the Header, Payload, and Veryfy section in the Deconded part, do you recognize any of the values?
10. Now, let us test whether the returned token can be used to grant us access to the protected endpoints in our project. Copy the returned token and paste it after the Bearer in the following request, you need to leave a space between Bearer and the token
... ### Protected endpoint - only admins (Role ADMIN) are allowed by Bearer token ### Do not forget to add the token you receive after signing in, which will allow you to visit this endpoint, but not the one dedicated for users with USER roles GET http://localhost:8090/api/auth/admin Content-Type: application/json Authorization: Bearer ...
11. Paste the token after the Bearer in the following request to check whether we can access an endpoint that can be accessed by users with "USER" roles. You should be prevented from accessing this endpoint as the provided token is for a user with the role ADMIN.
... ### Protected endpoint - only users (Role USER) are allowed by Bearer token GET http://localhost:8090/api/auth/user Content-Type: application/json Authorization: Bearer ...