In this section, we will cover how to implement JWT (JSON Web Token) authentication in a Spring Boot application. JWT is a popular method for securing APIs, as it allows for stateless authentication and is easy to use with modern web applications.
Key Concepts
- JWT Basics: Understanding what JWT is and how it works.
- Spring Security: Configuring Spring Security to use JWT.
- Creating JWT Tokens: Generating JWT tokens upon successful authentication.
- Validating JWT Tokens: Validating JWT tokens for protected routes.
- Practical Example: Implementing JWT authentication in a Spring Boot application.
JWT Basics
What is JWT?
JWT (JSON Web Token) is a compact, URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure.
Structure of JWT
A JWT consists of three parts:
- Header: Contains the type of token (JWT) and the signing algorithm (e.g., HMAC SHA256).
- Payload: Contains the claims. This is where the user data is stored.
- Signature: Used to verify the token's integrity and authenticity.
Example of a JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMSIsImV4cCI6MTYxNjIzOTAyMn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Spring Security Configuration
Dependencies
First, add the necessary dependencies to your pom.xml
file:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency>
Security Configuration
Create a security configuration class to configure Spring Security to use JWT:
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() .authorizeRequests() .antMatchers("/auth/**").permitAll() .anyRequest().authenticated() .and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); } @Bean public JwtAuthenticationFilter jwtAuthenticationFilter() { return new JwtAuthenticationFilter(); } }
Creating JWT Tokens
Authentication Controller
Create an authentication controller to handle login requests and generate JWT tokens:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/auth") public class AuthController { @Autowired private AuthenticationManager authenticationManager; @Autowired private JwtTokenProvider jwtTokenProvider; @PostMapping("/login") public String login(@RequestBody AuthRequest authRequest) { try { Authentication authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword()) ); UserDetails userDetails = (UserDetails) authentication.getPrincipal(); return jwtTokenProvider.createToken(userDetails.getUsername()); } catch (AuthenticationException e) { throw new RuntimeException("Invalid username/password"); } } }
JWT Token Provider
Create a utility class to generate and validate JWT tokens:
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.util.Date; @Component public class JwtTokenProvider { @Value("${jwt.secret}") private String secretKey; @Value("${jwt.expiration}") private long validityInMilliseconds; public String createToken(String username) { Claims claims = Jwts.claims().setSubject(username); Date now = new Date(); Date validity = new Date(now.getTime() + validityInMilliseconds); return Jwts.builder() .setClaims(claims) .setIssuedAt(now) .setExpiration(validity) .signWith(SignatureAlgorithm.HS256, secretKey) .compact(); } public boolean validateToken(String token) { try { Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token); return true; } catch (Exception e) { return false; } } public String getUsername(String token) { return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject(); } }
Validating JWT Tokens
JWT Authentication Filter
Create a filter to validate JWT tokens for protected routes:
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.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 jwtTokenProvider; @Autowired private UserDetailsService userDetailsService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String token = resolveToken(request); if (token != null && jwtTokenProvider.validateToken(token)) { String username = jwtTokenProvider.getUsername(token); UserDetails userDetails = userDetailsService.loadUserByUsername(username); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); } filterChain.doFilter(request, response); } private String resolveToken(HttpServletRequest request) { String bearerToken = request.getHeader("Authorization"); if (bearerToken != null && bearerToken.startsWith("Bearer ")) { return bearerToken.substring(7); } return null; } }
Practical Example
Application Properties
Add the following properties to your application.properties
file:
UserDetailsService Implementation
Implement the UserDetailsService
to load user-specific data:
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.Service; @Service public class CustomUserDetailsService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // Load user from database or any other source // For simplicity, returning a hardcoded user return org.springframework.security.core.userdetails.User .withUsername("user1") .password("{noop}password") .authorities("ROLE_USER") .build(); } }
Conclusion
In this section, we have covered the basics of JWT, how to configure Spring Security to use JWT, and how to create and validate JWT tokens. We also provided a practical example of implementing JWT authentication in a Spring Boot application. This knowledge will help you secure your APIs and ensure that only authenticated users can access protected resources.
Next, we will move on to testing in Spring Boot, where we will learn how to write unit and integration tests for our application.
Spring Boot Course
Module 1: Introduction to Spring Boot
- What is Spring Boot?
- Setting Up Your Development Environment
- Creating Your First Spring Boot Application
- Understanding Spring Boot Project Structure
Module 2: Spring Boot Basics
- Spring Boot Annotations
- Dependency Injection in Spring Boot
- Spring Boot Configuration
- Spring Boot Properties
Module 3: Building RESTful Web Services
- Introduction to RESTful Web Services
- Creating REST Controllers
- Handling HTTP Methods
- Exception Handling in REST
Module 4: Data Access with Spring Boot
- Introduction to Spring Data JPA
- Configuring Data Sources
- Creating JPA Entities
- Using Spring Data Repositories
- Query Methods in Spring Data JPA
Module 5: Spring Boot Security
- Introduction to Spring Security
- Configuring Spring Security
- User Authentication and Authorization
- Implementing JWT Authentication
Module 6: Testing in Spring Boot
Module 7: Advanced Spring Boot Features
Module 8: Deploying Spring Boot Applications
Module 9: Performance and Monitoring
- Performance Tuning
- Monitoring with Spring Boot Actuator
- Using Prometheus and Grafana
- Logging and Log Management