JWT In Spring Boot

JWT

Introduction To JWT

JWT (JSON Web Token) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.

Securing REST APIs with JWT in Spring Boot involves the following steps:

  1. Add Dependencies: Add Spring Security and JWT dependencies to your project.
  2. Configure Security: Configure Spring Security to use JWT for authentication and authorization.
  3. Create a JWT Token Provider: Generate and validate JWT tokens.
  4. Create a Filter: Intercept requests and validate JWT tokens.
  5. Secure Your Endpoints: Apply security to your REST API endpoints.

Add Dependency

For Maven

<dependencies>
    <!-- Spring Boot Starter Security -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- JWT library -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>
</dependencies>

For Gradle

dependencies {
    // Spring Boot Starter Security
    implementation 'org.springframework.boot:spring-boot-starter-security'
    
    // Spring Boot Starter Web
    implementation 'org.springframework.boot:spring-boot-starter-web'
    
    // JWT library
    implementation 'io.jsonwebtoken:jjwt:0.9.1'
}

Implementation

Step 1 : Configure Security

Create a security configuration class SecurityConfig.java. The SecurityConfig class configures Spring Security for our application.

  • @Configuration and @EnableWebSecurity: Annotate the class to configure security settings.
  • configure(HttpSecurity http): Configures HTTP security. We disable CSRF (for simplicity), set the session management to stateless (no server-side sessions), and define which endpoints are secured.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

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

    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter() {
        return new JwtAuthenticationFilter();
    }
}

Step 2 : Create a JWT Token Provider

Create a class JwtTokenProvider.java. The JwtTokenProvider class handles the creation and validation of JWTs.

  • generateToken(String username): Generates a JWT containing the username.
  • getUsernameFromJWT(String token): Extracts the username from the JWT.
  • validateToken(String authToken): Validates the JWT.
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
public class JwtTokenProvider {

    private final String JWT_SECRET = "your_secret_key";
    private final long JWT_EXPIRATION_MS = 86400000; // 1 day

    public String generateToken(String username) {
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + JWT_EXPIRATION_MS);

        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(expiryDate)
                .signWith(SignatureAlgorithm.HS512, JWT_SECRET)
                .compact();
    }

    public String getUsernameFromJWT(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(JWT_SECRET)
                .parseClaimsJws(token)
                .getBody();

        return claims.getSubject();
    }

    public boolean validateToken(String authToken) {
        try {
            Jwts.parser().setSigningKey(JWT_SECRET).parseClaimsJws(authToken);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

Step 3 : Create a JWT Authentication Filter

Create a class JwtAuthenticationFilter.java:

The JwtAuthenticationFilter class intercepts requests and validates JWTs.

  • doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain): Intercepts each request to check for a valid JWT.
  • getJwtFromRequest(HttpServletRequest request): Extracts the JWT from the request header.
import org.springframework.beans.factory.annotation.Autowired;
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.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.StringUtils;
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;

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private JwtTokenProvider tokenProvider;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        try {
            String jwt = getJwtFromRequest(request);

            if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
                String username = tokenProvider.getUsernameFromJWT(jwt);

                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                if (userDetails ! =null) {
                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            }
        } catch (Exception ex) {
            logger.error("Could not set user authentication in security context", ex);
        }

        filterChain.doFilter(request, response);
    }

    private String getJwtFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
}

Step 4 : Secure Your Endpoints

You can now secure your REST API endpoints by ensuring that the JWT is required for accessing them.

Here’s an example controller AuthController.java. The AuthController handles authentication requests.

  • authenticateUser(LoginRequest loginRequest): Authenticates the user and returns a JWT if successful.
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.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/auth")
public class AuthController {

    @Autowired
    AuthenticationManager authenticationManager;

    @Autowired
    JwtTokenProvider tokenProvider;

    @Autowired
    PasswordEncoder passwordEncoder;

    @PostMapping("/signin")
    public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest loginRequest)     
    {

        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        loginRequest.getUsername(),
                        loginRequest.getPassword()
                )
        );

        SecurityContextHolder.getContext().setAuthentication(authentication);

        String jwt = tokenProvider.generateToken(authentication.getName());
        return ResponseEntity.ok(new JwtAuthenticationResponse(jwt));
    }
}

Conclusion

Securing REST APIs is crucial in today’s interconnected world, and using JSON Web Tokens (JWT) provides a robust and scalable solution. By integrating JWT with Spring Boot, you can ensure that your API endpoints are protected, and only authorized users can access them. This guide covered the essential steps to achieve this:

  1. Adding Dependencies: Integrating necessary libraries for security and JWT.
  2. Configuring Security: Setting up Spring Security for stateless session management.
  3. Creating a Token Provider: Generating and validating JWT tokens.
  4. Implementing an Authentication Filter: Intercepting and validating incoming requests.
  5. Securing Endpoints: Ensuring that only authenticated requests reach your API.

By following these steps, you can build a secure, maintainable, and efficient authentication system for your Spring Boot application. This not only enhances the security of your application but also provides a seamless user experience by utilizing stateless authentication. With JWT, you have a powerful tool at your disposal to protect your API and ensure data integrity and security.

Explore our diverse collection of blogs covering a wide range of topics here.

Address

4232 Farnum Road, New York, New York(NY), 10029

Telephone: 212-289-5109

Mobile: 917-216-4839

Copyright © 2024 Learn Spring Boot Online