Spring Security : JWT Authentication

Implementing JWT Authentication with Spring Security

Implementing JWT Authentication
Implementing JWT Authentication

Introduction

In modern web applications, securing 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 choice for implementing authentication in web applications due to their stateless nature and ease of use. In this blog post, we will explore how to implement JWT authentication with Spring Security in a Java web application.


advertisement


What is JWT?

JWT is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. JWTs can be signed using a secret (with HMAC algorithm) or a public/private key pair (using RSA or ECDSA algorithms), which allows you to verify that the sender of the JWT is who it says it is and that the message has not been tampered with.

A JWT typically consists of three parts: a header, a payload, and a signature. The header specifies the type of token and the signing algorithm used, the payload contains the claims (e.g., user information, expiration time), and the signature is used to verify the authenticity of the token.

Implementing JWT Authentication with Spring Security

To implement JWT authentication with Spring Security, we need to perform the following steps:

1. Add Dependencies: Add the necessary dependencies to your pom.xml or build.gradle file:

   <!-- Spring Security -->
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-security</artifactId>
   </dependency>

   <!-- JWT -->
   <dependency>
       <groupId>io.jsonwebtoken</groupId>
       <artifactId>jjwt</artifactId>
       <version>0.9.1</version>
   </dependency>
   

2. Create a JWT Utility Class: Create a utility class to generate and validate JWTs. This class will typically have methods for creating a JWT, parsing a JWT, and validating a JWT.

   public class JwtUtil {
       private String secretKey = "your_secret_key_here";

       public String generateToken(String username) {
           return Jwts.builder()
                   .setSubject(username)
                   .setIssuedAt(new Date())
                   .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10 hours
                   .signWith(SignatureAlgorithm.HS256, secretKey)
                   .compact();
       }

       public Claims extractClaims(String token) {
           return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody();
       }

       public String extractUsername(String token) {
           return extractClaims(token).getSubject();
       }

       public boolean validateToken(String token) {
           try {
               Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
               return true;
           } catch (JwtException | IllegalArgumentException e) {
               return false;
           }
       }
   }
   


advertisement



3. Configure Spring Security: Configure Spring Security to use JWT for authentication. Create a class that extends WebSecurityConfigurerAdapter and override the configure method to configure JWT authentication.

   @Configuration
   @EnableWebSecurity
   public class SecurityConfig extends WebSecurityConfigurerAdapter {

       @Autowired
       private JwtRequestFilter jwtRequestFilter;

       @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);
       }
   }
   

4. Create a JWT Request Filter: Create a filter to intercept incoming requests and validate the JWT in the Authorization header.

   @Component
   public class JwtRequestFilter extends OncePerRequestFilter {

       @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) {

               if (jwtUtil.validateToken(jwt)) {

                   UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                           new UsernamePasswordAuthenticationToken(username, null, Collections.emptyList());
                   usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                   SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
               }
           }
           chain.doFilter(request, response);
       }
   }
   

5. Create a Controller: Create a controller to handle authentication requests.

   @RestController
   public class AuthController {

       @Autowired
       private JwtUtil jwtUtil;

       @Autowired
       private AuthenticationManager authenticationManager;

       @RequestMapping(value = "/authenticate", method = RequestMethod.POST)
       public ResponseEntity<?> createAuthenticationToken(@RequestBody AuthRequest 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.getUsername());

           return ResponseEntity.ok(new AuthResponse(jwt));
       }
   }
   

6. Test the Application: Test the application by sending a POST request to /authenticate with valid credentials. You should receive a JWT in the response, which you can then use to access protected resources by including it in the Authorization header of your requests as Bearer <token>.


advertisement


Conclusion

In this blog post, we have learned how to implement JWT authentication with Spring Security in a Java web application. JWT authentication provides a secure and efficient way to authenticate users and protect sensitive data. By following the steps outlined in this post, you can easily integrate JWT authentication into your Spring Security application.

Post a Comment

Previous Post Next Post