DEV Community

Cover image for Securing a REST API Created With Spring Boot 3 Using Spring Security with username-password And JWT Authentication
İbrahim Gündüz
İbrahim Gündüz

Posted on • Originally published at Medium

Securing a REST API Created With Spring Boot 3 Using Spring Security with username-password And JWT Authentication

Username-password and JWT-based authentication is a common way of securing an API. The authorization server creates a token after the first authentication and allows the client to access the endpoint with the generated token for subsequent requests. Today, we’ll learn how to implement Username-password and JWT-based authentication in a Spring Boot 3 application using JPA, MySQL, and Spring Security.

Table Of Contents:

Example Application

We’ll create an example application with login and an authentication-protected dummy endpoint. Let’s begin with defining dependencies.

Maven Configuration:

Create a pom.xml file like the one below in the project root:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>spring-security-jwt-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.4</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>8.2.0</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.12.5</version>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.name}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.12.1</version>
                <configuration>
                    <annotationProcessorPaths>
                        <annotationProcessorPath>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-configuration-processor</artifactId>
                        </annotationProcessorPath>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
Enter fullscreen mode Exit fullscreen mode

Database Preparations and Configuration

As we’ll access the users from the database, let’s create the following user entity and repository.

package org.example.persistence;

import jakarta.persistence.*;

@Entity
@Table(name = "users")
public class UserEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 50)
    private String fullName;

    @Column(nullable = false, length = 30, unique = true)
    private String username;

    @Column(nullable = false, length = 120)
    private String password;

    @Column(nullable = false, length = 20)
    private String role;

    private boolean enabled;

    public UserEntity() {
    }

    public UserEntity(Long id, String fullName, String username, String password, String role, boolean enabled) {
        this.id = id;
        this.fullName = fullName;
        this.username = username;
        this.password = password;
        this.role = role;
        this.enabled = enabled;
    }

    public Long getId() {
        return id;
    }

    public String getFullName() {
        return fullName;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }

    public String getRole() {
        return role;
    }

    public boolean isEnabled() {
        return enabled;
    }
}
Enter fullscreen mode Exit fullscreen mode
package org.example.persistence;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface UserRepository extends JpaRepository<UserEntity, Long> {
    Optional<UserEntity> findByUsername(String username);
}
Enter fullscreen mode Exit fullscreen mode

We’ll store the users in a MySQL database in the example application. So, create a configuration file named application.yml under the resources folder to tell Hibernate what database we use and how to access it. Since we created the application for demo purposes, we will upload the first user from a script file to the database.

spring:
  sql:
    init:
      # Just to load initial data for the demo. DO NOT USE IT IN PRODUCTION
      mode: always
  datasource:
    # Put your
    url: jdbc:mysql://localhost:3306/demo-dev
    username: devuser
    password: devpassword
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    database: mysql
    database-platform: org.hibernate.dialect.MySQLDialect
    # Just to load initial data for the demo. DO NOT USE IT IN PRODUCTION
    defer-datasource-initialization: true
    hibernate:
      ddl-auto: update
  jackson:
    serialization:
      indent-output: true

server:
  port: 3000
Enter fullscreen mode Exit fullscreen mode

To create an initial user, create a file named data.sql under the resources folder with the following content. The file will be loaded when the application starts.

truncate table `users`;

insert into `users` (`id`, `username`, `password`, `full_name`, `enabled`, `role`) values(1, 'user', '{bcrypt}$2a$10$IeofhAYT3lUfrF0bi1aflOat.IU3xOkZWaAWAuVc9jO2.QxTtH4RO', 'User', 1, 'USER');
-- for h2 database
-- alter table `users` alter column `id` restart with 2;

-- for mysql database
alter table `users` AUTO_INCREMENT = 2;
Enter fullscreen mode Exit fullscreen mode

Run the following command to create a MySQL container to be used in the development environment.

$ docker run --name mysql \
-e MYSQL_ROOT_PASSWORD=root \
-e MYSQL_DATABASE=demo-dev \
-e MYSQL_USER=devuser \
-e MYSQL_PASSWORD=devpassword \
-p 3306:3306 \
-d mysql:latest
Enter fullscreen mode Exit fullscreen mode

We’ve finished the database preparations. Let’s run the application and, see the created initial user on the database by running the following command.

$ docker exec -t mysql \
mysql \
-u devuser \
--password=devpassword \
-A demo-dev \
-e 'select * from users;'
Enter fullscreen mode Exit fullscreen mode

You should see an output like the one below:

mysql: [Warning] Using a password on the command line interface can be insecure.
+----+------------------+-----------+----------------------------------------------------------------------+------+----------+
| id | enabled          | full_name | password                                                             | role | username |
+----+------------------+-----------+----------------------------------------------------------------------+------+----------+
|  1 | 0x01             | User      | {bcrypt}$2a$10$IeofhAYT3lUfrF0bi1aflOat.IU3xOkZWaAWAuVc9jO2.QxTtH4RO | USER | user     |
+----+------------------+-----------+----------------------------------------------------------------------+------+----------+
Enter fullscreen mode Exit fullscreen mode

Configure Spring Security To Authenticate Users From The Database

Create an implementation of UserDetailsService interface like the following one to tell Spring Security how to load the user from the database.

package org.example.security;

import org.example.persistence.UserEntity;
import org.example.persistence.UserRepository;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

@Component
public class UserDetailsServiceImp implements UserDetailsService {
    private final UserRepository userRepository;

    public UserDetailsServiceImp(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserEntity user = userRepository.findByUsername(username).orElseThrow(() -> new UsernameNotFoundException("User not found"));

        return User.withUsername(user.getUsername())
                .password(user.getPassword())
                .roles(user.getRole())
                .disabled(!user.isEnabled())
                .build();
    }
}
Enter fullscreen mode Exit fullscreen mode

Create a configuration class like the following one to declare the access rules and required Java beans.

package org.example.config;

import io.jsonwebtoken.security.Keys;
import org.example.api.filter.JwtAuthFilter;
import org.example.security.JwtUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.crypto.SecretKey;

@Configuration
public class SecurityConfig {
    private final UserDetailsService userDetailsService;

    public SecurityConfig(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Bean
    public JwtUtil jwtUtil() {
        final SecretKey secretKey = Keys.hmacShaKeyFor(configProperties.getSecretKey().getBytes());
        return new JwtUtil(secretKey);
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
        return configuration.getAuthenticationManager();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf(AbstractHttpConfigurer::disable)
                .sessionManagement(configurer -> configurer
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(
                        configurer -> configurer
                                .requestMatchers("/login").permitAll()
                                .anyRequest().authenticated()
                )
                .authenticationProvider(authenticationProvider());

        return http.build();
    }

    private AuthenticationProvider authenticationProvider() {
        PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        final DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setPasswordEncoder(passwordEncoder);
        provider.setUserDetailsService(userDetailsService);
        return provider;
    }
}
Enter fullscreen mode Exit fullscreen mode

JWT Utility

Since we need to create and validate A JWT at some points, we’ll create a utility class to perform these actions more easily.

package org.example.security;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.function.Function;

public class JwtUtil {
    private final SecretKey secretKey;

    public JwtUtil(SecretKey secretKey) {
        this.secretKey = secretKey;
    }

    public String generate(String username, Integer ttlInMs) {
        return Jwts.builder()
                .subject(username)
                .issuedAt(new Date(System.currentTimeMillis()))
                .expiration(new Date(System.currentTimeMillis() + ttlInMs))
                .signWith(secretKey)
                .compact();
    }

    public String extractUsername(String token) throws JwtException {
        return extractClaim(token, Claims::getSubject);
    }

    public Date extractExpirationDate(String token) throws JwtException {
        return extractClaim(token, Claims::getExpiration);
    }

    public Date extractCreatedAt(String token) throws JwtException {
        return extractClaim(token, Claims::getIssuedAt);
    }

    public boolean isTokenValid(String token, String username) {
        return !isTokenExpired(token) && extractUsername(token).equals(username);
    }

    private boolean isTokenExpired(String token) throws JwtException {
        return extractExpirationDate(token).before(new Date());
    }

    private <T> T extractClaim(String token, Function<Claims, T> claimsResolvers) throws JwtException {
        final Claims claims = extractClaims(token);
        return claimsResolvers.apply(claims);
    }

    private Claims extractClaims(String token) throws JwtException {
        return Jwts.parser()
                .verifyWith(secretKey)
                .build()
                .parseSignedClaims(token)
                .getPayload();
    }
}
Enter fullscreen mode Exit fullscreen mode

Define the following configurations in the application.yml file.

#...
application:
  security:
    secret-key: 3MP8Xi8ExjXcPHbOO3wWLRHJDGqwK6XV
    jwt-ttl: 300000 # 5 minutes
Enter fullscreen mode Exit fullscreen mode

Create a class named SecurityConfigProperties like the following one to inject the custom configurations specified above wherever needed easily.

package org.example.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "application.security")
public class SecurityConfigProperties {
    private String secretKey;
    private Long jwtTtl;

    public String getSecretKey() {
        return secretKey;
    }

    public Long getJwtTtl() {
        return jwtTtl;
    }

    public void setSecretKey(String secretKey) {
        this.secretKey = secretKey;
    }

    public void setJwtTtl(Long jwtTtl) {
        this.jwtTtl = jwtTtl;
    }
}
Enter fullscreen mode Exit fullscreen mode

Create an instance of JwtUtil with the secret key specified in the configuration and register as a bean.

package org.example.config;

import io.jsonwebtoken.security.Keys;
import org.example.security.JwtUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.crypto.SecretKey;
//...

@Configuration
public class SecurityConfig {
    private final SecurityConfigProperties configProperties;
    private final UserDetailsService userDetailsService;

    public SecurityConfig(SecurityConfigProperties configProperties, UserDetailsService userDetailsService) {
        this.configProperties = configProperties;
        this.userDetailsService = userDetailsService;
    }

    //...

    @Bean
    public JwtUtil jwtUtil() {
        final SecretKey secretKey = Keys.hmacShaKeyFor(configProperties.getSecretKey().getBytes());
        return new JwtUtil(secretKey);
    }
   //...
}
Enter fullscreen mode Exit fullscreen mode

Create A Login Endpoint

Create the following request and response models.

package org.example.api.dto;

import jakarta.validation.constraints.NotBlank;

public class LoginRequest {
    @NotBlank
    private final String username;
    @NotBlank
    private final String password;

    public LoginRequest(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }
}
Enter fullscreen mode Exit fullscreen mode
package org.example.api.dto;

import java.time.LocalDate;
import java.util.Date;

public class LoginResponse {
    private final String token;
    private final Date createdAt;
    private final Date expiresAt;

    public LoginResponse(String token, Date createdAt, Date expiresAt) {
        this.token = token;
        this.createdAt = createdAt;
        this.expiresAt = expiresAt;
    }

    public String getToken() {
        return token;
    }

    public Date getCreatedAt() {
        return createdAt;
    }

    public Date getExpiresAt() {
        return expiresAt;
    }
}
Enter fullscreen mode Exit fullscreen mode

Create a login controller to handle the login requests.

package org.example.api.controller;

import jakarta.validation.Valid;
import org.example.JwtUtil;
import org.example.api.dto.LoginRequest;
import org.example.api.dto.LoginResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.web.bind.annotation.*;

@RestController
public class LoginController {
    private final AuthenticationManager authenticationManager;
    private final JwtUtil jwtUtil;
    private final Integer jwtTtl;

    public LoginController(
            AuthenticationManager authenticationManager,
            JwtUtil jwtUtil,
            @Value("{application.security.jwt-ttl}") Integer jwtTtl
    ) {
        this.authenticationManager = authenticationManager;
        this.jwtUtil = jwtUtil;
        this.jwtTtl = jwtTtl;
    }


    @ResponseStatus(HttpStatus.OK)
    @PostMapping("/login")
    public LoginResponse login(@RequestBody @Valid LoginRequest request) {
        UsernamePasswordAuthenticationToken authToken = UsernamePasswordAuthenticationToken.unauthenticated(request.getUsername(), request.getPassword());
        Authentication authentication = authenticationManager.authenticate(authToken);

        if (!authentication.isAuthenticated()) {
            throw new UsernameNotFoundException("Failed to authenticate");
        }

        String token = jwtUtil.generate(request.getUsername(), jwtTtl);
        return new LoginResponse(
                token,
                jwtUtil.extractCreatedAt(token),
                jwtUtil.extractExpirationDate(token)
        );
    }
}
Enter fullscreen mode Exit fullscreen mode

Now we have completed the part up to the authentication. Let’s run the application and perform the following authentication request.

$ curl -s -XPOST \
-H 'Content-Type: application/json' \
-d '{"username": "user", "password": "password"}' \
http://localhost:3000/login
Enter fullscreen mode Exit fullscreen mode

You should see an output like this:

{
  "token" : "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyIiwiaWF0IjoxNzE2NzU0MDY5LCJleHAiOjE3MTY3NTQzNjl9.g3clo5I801f1CHp5tIBWULH82HOSqDjyxFfymQqKAVY",
  "createdAt" : "2024-05-26T20:07:49.000+00:00",
  "expiresAt" : "2024-05-26T20:12:49.000+00:00"
}
Enter fullscreen mode Exit fullscreen mode

Authenticate Subsequent Requests With JWT

Create a filter named JwtAuthFilter to authenticate the requests with JWT in the Authorization request header.

package org.example.api.filter;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.example.security.JwtUtil;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Component
public class JwtAuthFilter extends OncePerRequestFilter {
    private final UserDetailsService userDetailsService;
    private final JwtUtil jwtUtil;

    private final String AUTH_HEADER = "Authorization";
    private final String AUTH_TYPE = "Bearer";

    public JwtAuthFilter(UserDetailsService userDetailsService, JwtUtil jwtUtil) {
        this.userDetailsService = userDetailsService;
        this.jwtUtil = jwtUtil;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        final String token = extractAuthorizationHeader(request);

        if (token == null) {
            filterChain.doFilter(request, response);
            return;
        }

        final String tokenUser = jwtUtil.extractUsername(token);

        if (tokenUser != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            final UserDetails userDetails = userDetailsService.loadUserByUsername(tokenUser);

            if (!jwtUtil.isTokenValid(token, tokenUser)) {
                throw new UsernameNotFoundException("Failed to authenticate with access token");
            }

            final SecurityContext context = SecurityContextHolder.createEmptyContext();
            final UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            context.setAuthentication(authToken);
            SecurityContextHolder.setContext(context);
        }

        filterChain.doFilter(request, response);
    }

    private String extractAuthorizationHeader(HttpServletRequest request) {
        final String headerValue = request.getHeader(AUTH_HEADER);

        if (headerValue == null || !headerValue.startsWith(AUTH_TYPE)) {
            return null;
        }

        return headerValue.substring(AUTH_TYPE.length()).trim();
    }
}
Enter fullscreen mode Exit fullscreen mode

As you could see from the code above, the filter allows the client to access the endpoint when the JWT from the Authorization header is valid and the user from the token exists in the user database.

Since we want requests targeting the authentication-protected endpoints to be handled by JwtAutFilter, we configure JwtAuthFilter to be called before UsernamePasswordAuthenticationFilter by the following configuration.

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, JwtAuthFilter jwtAuthFilter) throws Exception {
    http
            .csrf(AbstractHttpConfigurer::disable)
            .sessionManagement(configurer -> configurer
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authorizeHttpRequests(
                    configurer -> configurer
                            .requestMatchers("/login").permitAll()
                            .anyRequest().authenticated()
            )
            .authenticationProvider(authenticationProvider())
            .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);

    return http.build();
}
Enter fullscreen mode Exit fullscreen mode

Testing

Finally, we completed the implementation. As we reach the point that we‘re going to test the application, let’s create a dummy controller to simulate an authentication-protected endpoint.

package org.example.api.controller;

import org.example.api.dto.GreetingModel;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseStatus;

@Controller
public class GreetingController {
    @GetMapping("/greeting")
    @ResponseStatus(HttpStatus.OK)
    public GreetingModel sayHello() {
        return new GreetingModel("Hello World!");
    }
}
Enter fullscreen mode Exit fullscreen mode
package org.example.api.dto;

public class GreetingModel {
    private final String message;

    public GreetingModel(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}
Enter fullscreen mode Exit fullscreen mode

Run the application and execute the following call.

$ curl -I -H 'Content-Type: application/json' http://localhost:3000/greeting
Enter fullscreen mode Exit fullscreen mode

Because we called the endpoint without JWT, we should see a result like the following one.

HTTP/1.1 403
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Length: 0
Date: Mon, 27 May 2024 05:37:57 GMT
Enter fullscreen mode Exit fullscreen mode

Let’s authenticate and call the endpoint again with the JWT we received from the login response.

Execute the following call to authenticate the application

$ curl -s -XPOST \
-H 'Content-Type: application/json' \
-d '{"username": "user", "password": "password"}' \
http://localhost:3000/login
Enter fullscreen mode Exit fullscreen mode

And after the execution probably you would see something like this

{
  "token" : "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyIiwiaWF0IjoxNzE2Nzg4NjA2LCJleHAiOjE3MTY3ODg5MDZ9.FQG-wmli8vrBrJ7RLLlxlkXGFtA4RT3rRdjjjOrD3EQ",
  "createdAt" : "2024-05-27T05:43:26.000+00:00",
  "expiresAt" : "2024-05-27T05:48:26.000+00:00"
}
Enter fullscreen mode Exit fullscreen mode

Let’s try to call the dummy endpoint with our new token

$ curl \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyIiwiaWF0IjoxNzE2Nzg4NjA2LCJleHAiOjE3MTY3ODg5MDZ9.FQG-wmli8vrBrJ7RLLlxlkXGFtA4RT3rRdjjjOrD3EQ' \
http://localhost:3000/greeting
Enter fullscreen mode Exit fullscreen mode

You should see the same output as below

{
  "message" : "Hello World!"
}
Enter fullscreen mode Exit fullscreen mode

Just to not take the article longer, we haven’t touched the point regarding exception handler part, but I hope you enjoyed reading.

You can find the full source code of the example application on GitHub.

Thanks for reading!

Credits

Top comments (0)