Overview
JSON Web Token (JWT) is a compact and self-contained way of securely transmitting information between parties. In modern applications, it is commonly used for securing APIs by validating tokens sent by the client.
In this article, you’ll learn how to:
- Build a REST API secured with Spring Security and JWT
- Authenticate users and generate JWTs
- Validate incoming JWTs for protected routes
Tools & Dependencies
- Java 17+
- Spring Boot 3.x
- Maven
- Spring Web, Spring Security, Spring Boot Starter Data JPA
- H2 Database (for demo)
jjwt
for JWT operations
Project Structure
src/main/java/com/example/jwtsecurity/
├── controller/
│ └── AuthController.java
├── dto/
│ └── AuthRequest.java
│ └── AuthResponse.java
├── model/
│ └── User.java
├── repository/
│ └── UserRepository.java
├── security/
│ └── JwtFilter.java
│ └── JwtUtil.java
│ └── SecurityConfig.java
├── service/
│ └── UserService.java
├── JwtSecurityApplication.java
Step-by-Step Implementation
1. Add Dependencies (pom.xml
)
<dependencies>
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
2. User Entity
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String username;
private String password;
private String role;
}
3. DTOs
AuthRequest.java
public class AuthRequest {
private String username;
private String password;
}
AuthResponse.java
public class AuthResponse {
private String token;
public AuthResponse(String token) {
this.token = token;
}
public String getToken() {
return token;
}
}
4. JWT Utility
JwtUtil.java
@Component
public class JwtUtil {
private final String SECRET_KEY = "mysecret";
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60)) // 1 hour
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
public String extractUsername(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody()
.getSubject();
}
public boolean validateToken(String token, UserDetails userDetails) {
String username = extractUsername(token);
return (username.equals(userDetails.getUsername()));
}
}
5. Security Configuration
SecurityConfig.java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
private JwtFilter jwtFilter;
@Autowired
private UserService userService;
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeHttpRequests(auth -> auth
.requestMatchers("/auth").permitAll()
.anyRequest().authenticated()
)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
6. JWT Filter
JwtFilter.java
@Component
public class JwtFilter extends OncePerRequestFilter {
@Autowired
private JwtUtil jwtUtil;
@Autowired
private UserService userService;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
final String authHeader = request.getHeader("Authorization");
String username = null;
String jwt = null;
if (authHeader != null && authHeader.startsWith("Bearer ")) {
jwt = authHeader.substring(7);
username = jwtUtil.extractUsername(jwt);
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userService.loadUserByUsername(username);
if (jwtUtil.validateToken(jwt, userDetails)) {
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
token.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(token);
}
}
filterChain.doFilter(request, response);
}
}
7. User Service
UserService.java
@Service
public class UserService implements UserDetailsService {
@Autowired
private UserRepository userRepo;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepo.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
List.of(new SimpleGrantedAuthority(user.getRole()))
);
}
}
8. Authentication Endpoint
AuthController.java
@RestController
public class AuthController {
@Autowired
private AuthenticationManager authManager;
@Autowired
private JwtUtil jwtUtil;
@Autowired
private UserService userService;
@PostMapping("/auth")
public ResponseEntity<AuthResponse> authenticate(@RequestBody AuthRequest request) {
try {
authManager.authenticate(
new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()));
} catch (AuthenticationException e) {
throw new RuntimeException("Invalid Credentials");
}
String token = jwtUtil.generateToken(request.getUsername());
return ResponseEntity.ok(new AuthResponse(token));
}
@GetMapping("/hello")
public String hello() {
return "Hello, Authenticated User!";
}
}
9. Data Initialization (Optional)
CommandLineRunner for demo
@Bean
CommandLineRunner init(UserRepository repo, PasswordEncoder encoder) {
return args -> {
User user = new User();
user.setUsername("user");
user.setPassword(encoder.encode("password"));
user.setRole("ROLE_USER");
repo.save(user);
};
}
Example Flow
- Send
POST /auth
with JSON:{ "username": "user", "password": "password" }
- Receive JWT token in response.
- Access protected route
/hello
using:Authorization: Bearer <token>
Conclusion
Using JWT with Spring Boot Security is a clean, stateless way to protect your REST APIs. By decoupling session management, this architecture scales better for modern microservices and SPAs.