Spring Boot 3 and JWT Application
It just a start...

Spring Boot 3 and JWT Application

Implementing a Spring Boot 3 application with JWT (JSON Web Token) for authentication involves several key steps. This guide will walk you through the process step by step. It assumes that you have a basic understanding of Spring Boot, Spring Security, and JWT concepts.

Authentication Flow Diagram

This diagram represents the flow of how a user authenticates using a username and password and how the JWT is generated and validated.

2. Request Filtering with JWT

This diagram illustrates how the JWT is extracted from the request and validated via a filter before accessing protected resources.


3. Class Diagram of Key Components

This diagram shows the main classes and their relationships within the application.


4. Sequence Diagram for Token Validation

This sequence diagram shows the process of validating a JWT when a user tries to access a protected resource.


5. Database Interaction Diagram

This diagram shows how the UserDetailsService interacts with the database (via UserRepository) during the authentication process.


Summary of UML Diagrams:

  • Authentication Flow Diagram: Shows how the authentication process works, from the user’s login request to JWT generation.
  • Request Filtering with JWT: Shows how the request filter checks the JWT and validates the token before allowing access to secured resources.
  • Class Diagram: Illustrates the key classes involved in the JWT implementation.
  • Token Validation Sequence Diagram: Describes the flow of validating the JWT when accessing protected resources.
  • Database Interaction Diagram: Depicts how the UserDetailsService interacts with the UserRepository during authentication.

These diagrams offer a visual understanding of the various components and interactions involved in the Spring Boot 3 JWT implementation. You can paste these diagrams into a .puml file or use an online PlantUML editor to render them.

1. Set Up the Spring Boot 3 Project

  • Use Spring Initializr to generate a Spring Boot 3 project:
  • Import the project into your favorite IDE (e.g., IntelliJ IDEA, Eclipse, or VS Code).

2. Configure Maven Dependencies for JWT

Edit the pom.xml file to include dependencies for JWT and any additional dependencies you may need.

xml

<dependencies>

    <!-- Spring Boot Starter Web -->

    <dependency>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-web</artifactId>

    </dependency>

    <!-- Spring Boot Starter Security -->

    <dependency>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-security</artifactId>

    </dependency>

    <!-- JWT Auth Dependency -->

    <dependency>

        <groupId>io.jsonwebtoken</groupId>

        <artifactId>jjwt</artifactId>

        <version>0.9.1</version>

    </dependency>

    <!-- Spring Boot Starter Data JPA -->

    <dependency>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-data-jpa</artifactId>

    </dependency>

    <!-- H2 Database for Development -->

    <dependency>

        <groupId>com.h2database</groupId>

        <artifactId>h2</artifactId>

        <scope>runtime</scope>

    </dependency>

    <!-- Lombok -->

    <dependency>

        <groupId>org.projectlombok</groupId>

        <artifactId>lombok</artifactId>

        <scope>provided</scope>

    </dependency>

</dependencies>

3. Create the User Entity

Define a User entity that will hold the user information and roles. You can also define Role if necessary for RBAC (Role-Based Access Control).

java

@Entity

@Data

@NoArgsConstructor

@AllArgsConstructor

public class User {

    @Id

    @GeneratedValue(strategy = GenerationType.IDENTITY)

    private Long id;

    

    private String username;

    private String password;

    private String roles; // For simplicity, you can store roles as a comma-separated string.

    

    // Add constructors, getters, setters, etc.

}

4. Create a UserRepository

Create a repository interface for database interactions:

java

@Repository

public interface UserRepository extends JpaRepository<User, Long> {

    Optional<User> findByUsername(String username);

}

5. Create a JWT Utility Class

This class will handle the generation and validation of JWT tokens.

java

import io.jsonwebtoken.Claims;

import io.jsonwebtoken.Jwts;

import io.jsonwebtoken.SignatureAlgorithm;

import org.springframework.stereotype.Component;

import java.util.Date;

import java.util.HashMap;

import java.util.Map;

import java.util.function.Function;

@Component

public class JwtUtil {

    private String SECRET_KEY = "mysecret"; // Use environment variables for production!

    public String extractUsername(String token) {

        return extractClaim(token, Claims::getSubject);

    }

    public Date extractExpiration(String token) {

        return extractClaim(token, Claims::getExpiration);

    }

    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {

        final Claims claims = extractAllClaims(token);

        return claimsResolver.apply(claims);

    }

    private Claims extractAllClaims(String token) {

        return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();

    }

    private Boolean isTokenExpired(String token) {

        return extractExpiration(token).before(new Date());

    }

    public String generateToken(UserDetails userDetails) {

        Map<String, Object> claims = new HashMap<>();

        return createToken(claims, userDetails.getUsername());

    }

    private String createToken(Map<String, Object> claims, String subject) {

        return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))

                .setExpiration(new Date(System.currentTimeMillis() + 1000 60 60 * 10))

                .signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();

    }

    public Boolean validateToken(String token, UserDetails userDetails) {

        final String username = extractUsername(token);

        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));

    }

}

6. Create Custom UserDetailsService

This service will fetch the user data from the database for authentication purposes.

java

@Service

public class MyUserDetailsService implements UserDetailsService {

    @Autowired

    private UserRepository userRepository;

    @Override

    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        User user = userRepository.findByUsername(username)

                                  .orElseThrow(() -> new UsernameNotFoundException("User not found"));

        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), 

                new ArrayList<>());

    }

}

7. Create a JWT Request Filter

This filter will intercept each request, check for a JWT in the header, and authenticate the user if the token is valid.

java

@Component

public class JwtRequestFilter extends OncePerRequestFilter {

    @Autowired

    private MyUserDetailsService userDetailsService;

    @Autowired

    private JwtUtil jwtUtil;

    @Override

    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)

            throws ServletException, IOException {

        final String authorizationHeader = request.getHeader("Authorization");

        String username = null;

        String jwt = null;

        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {

            jwt = authorizationHeader.substring(7);

            username = jwtUtil.extractUsername(jwt);

        }

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {

            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);

            if (jwtUtil.validateToken(jwt, userDetails)) {

                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(

                        userDetails, null, userDetails.getAuthorities());

                authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

                SecurityContextHolder.getContext().setAuthentication(authenticationToken);

            }

        }

        chain.doFilter(request, response);

    }

}

8. Configure Spring Security (continued)

Update the Spring Security configuration to allow JWT-based authentication and authorize requests accordingly:

java

@EnableWebSecurity

public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired

    private MyUserDetailsService myUserDetailsService;

    @Autowired

    private JwtRequestFilter jwtRequestFilter;

    @Override

    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth.userDetailsService(myUserDetailsService);

    }

    @Override

    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable()

            .authorizeRequests()

                .antMatchers("/authenticate").permitAll()  // Allow access to authenticate endpoint

                .anyRequest().authenticated()  // All other endpoints are secured

            .and()

            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // Add the JWT filter before processing requests

        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);

    }

    @Override

    @Bean

    public AuthenticationManager authenticationManagerBean() throws Exception {

        return super.authenticationManagerBean();

    }

}

9. Create Authentication Controller

Create a controller that exposes an endpoint to authenticate users and return a JWT.

java

@RestController

public class AuthenticationController {

    @Autowired

    private AuthenticationManager authenticationManager;

    @Autowired

    private JwtUtil jwtUtil;

    @Autowired

    private MyUserDetailsService userDetailsService;

    @PostMapping("/authenticate")

    public ResponseEntity<?> createAuthenticationToken(@RequestBody AuthenticationRequest authRequest) throws Exception {

        try {

            authenticationManager.authenticate(

                new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword())

            );

        } catch (BadCredentialsException e) {

            throw new Exception("Incorrect username or password", e);

        }

        final UserDetails userDetails = userDetailsService.loadUserByUsername(authRequest.getUsername());

        final String jwt = jwtUtil.generateToken(userDetails);

        return ResponseEntity.ok(new AuthenticationResponse(jwt));

    }

}

10. Create AuthenticationRequest and AuthenticationResponse Models

These classes are used to handle the incoming request for authentication and to return the JWT token as a response.

  • AuthenticationRequest (to handle login credentials):

java

public class AuthenticationRequest {

    private String username;

    private String password;

    // Getters and setters

}

  • AuthenticationResponse (to return the JWT token):

java

public class AuthenticationResponse {

    private final String jwt;

    public AuthenticationResponse(String jwt) {

        this.jwt = jwt;

    }

    public String getJwt() {

        return jwt;

    }

}

11. Application Properties Configuration

Add any necessary configuration in the application.properties file, such as setting up your database connection:

properties

# H2 Database (for dev)

spring.h2.console.enabled=true

spring.datasource.url=jdbc:h2:mem:testdb

spring.datasource.driverClassName=org.h2.Driver

spring.datasource.username=sa

spring.datasource.password=password

spring.jpa.hibernate.ddl-auto=update

# Secret key for JWT can be externalized here as well:

jwt.secret=mysecret

12. Testing the Application

1. Run the Spring Boot Application:

Run the Spring Boot application, ensuring that all components (like the database and security) are correctly set up.

2. Test the Authentication:

  • Use Postman or curl to make a POST request to the /authenticate endpoint:

bash

POST /authenticate

Content-Type: application/json

{

    "username": "your-username",

    "password": "your-password"

}

  • The response should contain a JWT token:

json

{

    "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI..."

}

3. Access Protected Endpoints:

Now, use the JWT token in the Authorization header (as Bearer token) to access protected endpoints:

bash

GET /some-protected-endpoint

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI...

13. Finalizing

  • Handle Token Expiration: You can modify the expiration time in JwtUtil to suit your requirements.
  • Error Handling: Ensure you have proper error handling for exceptions like invalid tokens or expired tokens.

Summary of Steps:

  1. Set up the Spring Boot project.
  2. Add JWT dependencies.
  3. Create a User entity and repository.
  4. Implement the JwtUtil for token handling.
  5. Implement UserDetailsService for user data fetching.
  6. Create a JwtRequestFilter to process requests.
  7. Configure Spring Security for JWT-based authentication.
  8. Create an authentication endpoint in the controller.
  9. Test the application using tools like Postman.

With these steps, you have successfully implemented a Spring Boot 3 application with JWT authentication!


spring




Ravikumar Maddi

Senior Solutions Architect

2mo

Hi All, thanks for visiting my article.

Like
Reply

To view or add a comment, sign in

Insights from the community

Others also viewed

Explore topics