Your Search Bar For Shrewd Tips

How To Add Jwt Authentication In Spring Boot


How To Add JWT Authentication In Spring Boot

In today's modern web applications, securing your API endpoints is crucial to protect sensitive data and ensure that only authorized users can access certain resources. JSON Web Tokens (JWT) have become a popular method for implementing stateless authentication due to their simplicity and scalability. If you're developing a Spring Boot application and want to integrate JWT authentication, this comprehensive guide will walk you through the process step-by-step. By the end of this article, you'll know how to add JWT authentication seamlessly into your Spring Boot project.

Understanding JWT Authentication

JSON Web Tokens (JWT) are compact, URL-safe tokens that encode JSON objects. They are used to securely transmit information between parties as a JSON object. JWTs consist of three parts:

  • Header: Contains metadata about the token, such as the signing algorithm.
  • Payload: Contains the claims or data you want to transmit, such as user details.
  • Signature: Verifies the authenticity of the token, created by signing the header and payload with a secret key.

In JWT-based authentication, when a user logs in successfully, the server issues a token which the client stores and includes in subsequent requests, typically in the Authorization header. The server then verifies the token's validity on each request, granting access accordingly.

Prerequisites and Setup

Before diving into coding, ensure you have the following:

  • Java Development Kit (JDK) 8 or higher installed
  • Spring Boot 2.x or later project setup
  • Build tool: Maven or Gradle
  • Basic knowledge of Spring Security and REST APIs

In this guide, we'll use Maven for dependency management. Ensure your project has Spring Boot dependencies included. You can generate a base project from Spring Initializr with Web and Security dependencies selected.

Adding Dependencies

To implement JWT authentication, you'll need to add some dependencies to your project. The most common is the jjwt library for handling JWT tokens. Here's how to include it in your Maven pom.xml:

<dependencies>
  <dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
</dependencies>

These dependencies provide the core Spring Security framework and JWT handling capabilities.

Configuring Spring Security

Spring Security needs to be configured to handle JWT authentication. We'll define a custom filter to intercept requests and validate tokens. Here's how:

import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public JwtRequestFilter jwtRequestFilter() {
        return new JwtRequestFilter();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // Configure your user details service or in-memory authentication here
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
                .antMatchers("/authenticate").permitAll()
                .anyRequest().authenticated()
            .and()
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        http.addFilterBefore(jwtRequestFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

This configuration disables CSRF (since we're stateless), permits unauthenticated access to the login endpoint, and applies our custom JWT filter to all other requests.

Creating the JWT Utility Class

To generate and validate tokens, create a utility class that encapsulates JWT operations:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

public class JwtUtil {

    private String SECRET_KEY = "your_secret_key"; // Replace with a secure key

    public String generateToken(String username) {
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, username);
    }

    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)) // 10 hours
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .compact();
    }

    public Boolean validateToken(String token, String username) {
        final String extractedUsername = extractUsername(token);
        return (extractedUsername.equals(username) && !isTokenExpired(token));
    }

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

    public Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    public  T extractClaim(String token, Function 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());
    }
}

This class helps generate tokens with a username claim, validate tokens, and extract claims from existing tokens.

Implementing the Authentication Endpoint

Next, create a REST controller to handle user login and token issuance:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.web.bind.annotation.*;

@RestController
public class AuthController {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtUtil jwtUtil;

    @PostMapping("/authenticate")
    public ResponseEntity<Map<String, String>> createAuthenticationToken(@RequestBody AuthRequest authRequest) {
        try {
            authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword())
            );
        } catch (AuthenticationException e) {
            return ResponseEntity.status(401).build();
        }

        final String jwt = jwtUtil.generateToken(authRequest.getUsername());

        Map<String, String> response = new HashMap<>();
        response.put("token", jwt);
        return ResponseEntity.ok(response);
    }

    // Inner class for authentication request payload
    public static class AuthRequest {
        private String username;
        private String password;

        // Getters and setters
        public String getUsername() { return username; }
        public void setUsername(String username) { this.username = username; }
        public String getPassword() { return password; }
        public void setPassword(String password) { this.password = password; }
    }
}

This controller exposes an endpoint at /authenticate that verifies user credentials and returns a JWT token upon success.

Creating the JWT Filter

The filter intercepts incoming requests, extracts the JWT token from the Authorization header, validates it, and sets the security context accordingly:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class JwtRequestFilter extends OncePerRequestFilter {

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private UserDetailsService userDetailsService;

    @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);
            try {
                username = jwtUtil.extractUsername(jwt);
            } catch (Exception e) {
                // invalid token
            }
        }

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
            if (jwtUtil.validateToken(jwt, userDetails.getUsername())) {
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities()
                );
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }
        chain.doFilter(request, response);
    }
}

This filter ensures only valid tokens are accepted and user details are loaded into the security context for further authorization.

Testing Your JWT Authentication

Once everything is configured, you can test your setup as follows:

  • Start your Spring Boot application.
  • Send a POST request to /authenticate with JSON payload containing username and password:
POST /authenticate
Content-Type: application/json

{
  "username": "user1",
  "password": "password123"
}

If credentials are valid, you'll receive a JSON response with the JWT token:

{
  "token": "eyJhbGciOiJIUzI1NiJ9..."
}
  • Use this token in subsequent requests by including it in the Authorization header:
GET /protected-endpoint
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

The server will verify the token and grant access if it's valid.

Best Practices and Tips

  • Secure your secret key: Use a strong, unpredictable key for signing tokens and do not expose it.
  • Token expiration: Set appropriate expiration times to reduce risk of token theft.
  • Refresh tokens: Implement refresh tokens for longer sessions without compromising security.
  • Secure transmission: Always use HTTPS to encrypt data in transit.
  • Handle errors gracefully: Provide meaningful error messages and avoid revealing sensitive information.

Conclusion

Integrating JWT authentication into your Spring Boot application enhances security by providing a stateless and scalable way to manage user sessions. This guide covered all the essential steps, including dependencies, security configuration, token generation, validation, and request filtering. With JWT, your APIs become more secure and easier to maintain, especially in distributed systems or microservices architectures. Start implementing JWT in your projects today to protect your resources and improve your application's security posture.


Disclaimer: Articles are written by Humans, AI or Both. Verify Important information.

Shrewdnia

Shrewdnia

Shrewdnia is a destination for curious minds seeking clarity, knowledge, and informed perspectives. Through insightful articles and practical guides our passionate team explores a wide range of topics designed to help readers understand the world around them, make smarter decisions, and stay informed in an ever-changing landscape.


💡 Every question sparks discovery, and every perspective enriches the conversation. Share your thoughts and insights in the comments 👇

Back to blog

Leave a comment

JOIN THE SHREWDNIA COMMUNITY FORUM

What do you think?

Have an opinion, experience, or question about this topic? Join the Shrewdnia Forum and share your thoughts with other readers.

Join the Forum →