Securing REST APIs with Spring Security (JWT)
When building REST APIs, securing endpoints is critical to prevent unauthorized access and data breaches. Spring Security, a powerful and customizable framework, simplifies securing APIs by providing authentication, authorization, and application-wide security configurations. When paired with JSON Web Tokens (JWT), it enables stateless authentication, perfect for modern microservices architectures.
This guide explores the essentials of securing REST APIs with Spring Security and JWT, including an overview of Spring Security, the process of generating and validating JWT tokens, configuring authentication filters, and protecting sensitive endpoints.
Table of Contents
- Overview of Spring Security
- JWT Token Generation and Validation
- Configuring Authentication Filter
- Protecting Endpoints
- External Resources for Further Learning
- Final Thoughts
Overview of Spring Security
What is Spring Security?
Spring Security is a robust, flexible framework for securing Java applications, particularly web applications and REST APIs. By default, it supports basic authentication, role-based access control, and integration with modern protocols like OAuth2 and JWT.
Key Features
- Authentication and Authorization: Supports various authentication methods (e.g., OAuth, session-based, token-based).
- Integration with Spring Boot: Spring Boot auto-configures most security dependencies.
- Extensibility: Fully customizable to meet complex security requirements.
For REST APIs, Spring Security facilitates stateless authentication by disabling sessions and relying on token-based mechanisms like JWT.
Why Use Spring Security with JWT?
- Stateless Authentication: JWT allows APIs to manage authentication without storing session information on the server.
- Scalability: Stateless design ensures tokens can be verified across distributed systems without shared state.
- Interoperability: JWT is widely adopted and compatible with many systems and platforms.
JWT Token Generation and Validation
What is a JSON Web Token (JWT)?
A JWT is a compact, URL-safe token that encodes claims in JSON format. It consists of three parts:
- Header: Specifies the algorithm and token type.
- Payload: Contains claims (e.g., user ID, roles).
- Signature: Ensures data integrity using a secret key.
A typical JWT structure looks like this:
Header.Payload.Signature
Generating a JWT Token
To generate tokens in Spring Boot, use libraries like JJWT (Java JWT). Add the dependency in your pom.xml
:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
</dependency>
JWT Generation Code
JwtUtil.java
package com.example.demo.util;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import java.security.Key;
import java.util.Date;
public class JwtUtil {
private static final Key SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256);
private static final long EXPIRATION_TIME = 1000 * 60 * 15; // 15 minutes
public static String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SECRET_KEY)
.compact();
}
}
Validating a JWT Token
JwtUtil.java (Add Validation Method):
public static boolean validateToken(String token) {
try {
Jwts.parserBuilder().setSigningKey(SECRET_KEY).build().parseClaimsJws(token);
return true;
} catch (Exception e) {
return false;
}
}
public static String extractUsername(String token) {
return Jwts.parserBuilder().setSigningKey(SECRET_KEY).build()
.parseClaimsJws(token)
.getBody()
.getSubject();
}
Configuring Authentication Filter
To intercept requests and validate JWT tokens, configure an Authentication Filter that integrates with Spring’s security ecosystem.
Creating a Custom Filter
JwtAuthenticationFilter.java
package com.example.demo.security;
import com.example.demo.util.JwtUtil;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
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 {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String authorizationHeader = request.getHeader("Authorization");
if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
String token = authorizationHeader.substring(7);
if (JwtUtil.validateToken(token)) {
String username = JwtUtil.extractUsername(token);
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(username, null, null);
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authToken);
}
filterChain.doFilter(request, response);
}
}
Applying the Filter in Security Configuration
SecurityConfig.java
package com.example.demo.config;
import com.example.demo.security.JwtAuthenticationFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
JwtAuthenticationFilter jwtFilter = new JwtAuthenticationFilter();
http.csrf().disable()
.authorizeHttpRequests()
.antMatchers("/auth/**").permitAll() // No authentication required
.anyRequest().authenticated()
.and()
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
}
Protecting Endpoints
Spring Security restricts access to endpoints based on authentication and role-based authorization.
Example Controller
UserController.java
package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@GetMapping("/public")
public String publicEndpoint() {
return "This endpoint is public!";
}
@GetMapping("/private")
public String privateEndpoint() {
return "This endpoint is restricted!";
}
}
Configuring Endpoint Protection
Update your security configuration to specify authorization rules:
http.authorizeHttpRequests()
.antMatchers("/public").permitAll()
.antMatchers("/private").authenticated();
Testing the API
- Obtain a JWT token by implementing an authentication endpoint (e.g.,
/auth/login
). - Use the token to access protected endpoints:
- Set the
Authorization
header:
Authorization: Bearer <JWT-TOKEN>
- Set the
- If valid, Spring Security grants access to
/private
.
External Resources for Further Learning
Final Thoughts
Securing REST APIs with Spring Security and JWT is essential for protecting sensitive data and ensuring only authorized users access your resources. Through features like token generation, validation, and custom authentication filters, Spring simplifies the implementation of stateless security for modern applications.
With this guide, you’ve learned the fundamentals of securing REST APIs with Spring Boot. Use it as a reference to build robust, secure APIs, and explore advanced techniques as your application grows. Bookmark this guide for all your Spring Security needs!