- • Quickstart
- • Documentation
- • Documentation DevOps
- • Next Steps
- • Spring Boot
- • Spring Data
- • Spring Data MongoDB
- › Spring Security
- • Frontend
- • Multi-Module
Last updated: 2024-02-14
Securing a REST API with Spring Security and JWT
The Bootify Builder can generate you a runnable Spring Boot application - with your custom database schema, REST API and Spring Security with JWT. With up-to-date, clean code - and days or weeks of saved development time.
Spring Security is the de facto standard for securing Spring Boot applications. JSON Web Token (JWT) is a good choice for protecting a REST API - the following article will show the minimal steps to setup a Spring Boot application with JWT.
The concept of JWT
As a first step, a client must authenticate itself using a username and password, receiving a signed token (JWT) in exchange. This token is stored locally at the client and is passed to the server with every further request, typically in the header. Since the token is signed using a key that only the server knows, the token and thus the client can be validated safely.
This approach makes the whole process stateless and very suitable for REST APIs, since no data about the state of the client (e.g. a session) needs to be stored. The username and the expiration date of the token are stored in the payload.
Example Payload of a JSON Web Token
Spring Security does not provide direct support for JWT, so a number of additions to our Spring Boot application are necessary. In our
pom.xml, we need to add the following two dependencies. Using the
java-jwt library, we will later generate and validate the tokens.
Adding new dependencies
The following controller defines the first step from a client's perspective: an endpoint for authentication to obtain a valid token. To keep the code snippet short, the constructor with the field assignments is omitted.
Authentication endpoint defined in our RestController
We also need to following request and response objects.
The request is validated and then passed to the authenticationManager for authentication. If successful, the JSON web token is generated and returned. There are no further details in the response, since the token itself should contain all relevant information.
As already referenced in our controller, the
JwtTokenService is responsible for generating and validating the token. We store the secret used for these tasks in our
application.yml under the key
jwt.secret. Make sure the key is at least 512 bits long, as this is required for this algorithm.
JwtTokenService encapsulating token handling
We also provide an implementation of the UserDetailsService interface that is accessed by the AuthenticationManager - which we configure later on. We access a table
client using the
ClientRepository, but any other source can be used here. We only assign the role
ROLE_USER, although different roles and permissions could be used at this point as well.
Implementation of UserDetailsService
We're also using our own implementation of the
UserDetails interface by extending the Spring Security
org.springframework.security.core.userdetails.User class. While this is not strictly needed, it allows us to keep the primary key within the authentication details.
Extension of UserDetails
Authentication of the requests
To authenticate the requests going to our REST API, we need to define
JwtRequestFilter. This filter ensures that a valid token is passed in the header and will store the
UserDetails in the
SecurityContext for the duration of the request.
JwtRequestFilter to validate tokens
The following header must be present at the request so that our filter can validate the JWT and authenticate the request.
Example JWT submitted as a header
There is also the possibility to use the
BearerTokenAuthenticationFilter of the library
spring-boot-starter-oauth2-resource-server. However using our own filter gives us more flexibility and the user is loaded with its current data and roles from the
JwtUserDetailsService on every request.
Spring Security config
This leads us to the heart of the matter, the configuration of Spring Security, which brings together all the previous components. Since Spring Boot 3 we should return the
SecurityFilterChain and don't use the
WebSecurityConfigurerAdapter anymore. With the current version 3.2.3 we should always define a Lamda or use
withDefaults() for configuring each part of our config.
Spring Security configuration for JWT
JwtUserDetailsService is to be used by the
AuthenticationManager, along with a
PasswordEncoder based on BCrypt. The
PasswordEncoder is exposed as a bean, so it can be used at other parts of our application as well, for example when registering a user and creating the hash from his password.
requestMatchers("/authenticate").permitAll() our authentication endpoint is freely accessible, but because of
anyRequest().hasAuthority(UserRoles.ROLE_USER) the role
ROLE_USER is required for all other request. This would be similar to
hasRole(UserRoles.USER), where Spring Security would automatically add the ROLE_ prefix.
SessionCreationPolicy.STATELESS is used to specify that Spring Security does not create or access a session.
JwtRequestFilter is added at a proper position in our filter chain, so the
SecurityContext is updated before the required role is actually checked.
With this minimal setup, our application is secured using Spring Security and JWT. It can be extended according to our own requirements, for example to define the required roles directly at our endpoints with
In the Professional plan of Bootify, a Spring Boot application with JWT setup can be generated using a table of your self-defined database schema. It can be specified whether annotations or the config are used for defining the protected endpoints. The roles can be loaded from a constant, an enum or from the database.