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:
- Add Dependencies: Add Spring Security and JWT dependencies to your project.
- Configure Security: Configure Spring Security to use JWT for authentication and authorization.
- Create a JWT Token Provider: Generate and validate JWT tokens.
- Create a Filter: Intercept requests and validate JWT tokens.
- 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:
- Adding Dependencies: Integrating necessary libraries for security and JWT.
- Configuring Security: Setting up Spring Security for stateless session management.
- Creating a Token Provider: Generating and validating JWT tokens.
- Implementing an Authentication Filter: Intercepting and validating incoming requests.
- 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.