🔐 Spring Security: JWT Authentication Guide
JWT (JSON Web Token) is a stateless token format widely used for authentication and authorization in modern Java applications. Unlike opaque tokens, JWT contains all necessary user information (claims) and a signature, allowing the server to validate requests without storing session state.
This guide shows a clean implementation using Spring Security for developers who want a practical approach.
🔍 JWT Authentication Flow
- Login: Client sends username/password to
/login
. - Token Issuance: Server validates credentials and returns a JWT.
- Authenticated Requests: Client includes JWT in
Authorization: Bearer <token>
header. - Token Validation: Server validates the token, extracts user info, and sets the
Authentication
inSecurityContext
.
Client Server
| |
| POST /login |
|-----------------> |
| username/pass |
| <---------------- |
| JWT token |
| |
| GET /api/resource |
|-----------------> |
| Authorization: |
| Bearer <JWT> |
| <---------------- |
| 200 OK / Data |
🛠️ Example Spring Security Configuration with JWT
JWT Service
@Service
public class JwtService {
private static final String ROLES_CLAIM = "roles";
private final Algorithm signingAlgorithm;
public JwtService(@Value("${jwt.signing-secret}") String signingSecret) {
this.signingAlgorithm = Algorithm.HMAC256(signingSecret);
}
public AuthUser resolveJwtToken(String token) {
DecodedJWT decodedJWT = JWT.require(signingAlgorithm).build().verify(token);
String userId = decodedJWT.getSubject();
List<Role> roles = decodedJWT.getClaim(ROLES_CLAIM).asList(Role.class);
return new AuthUser(userId, roles);
}
public String createJwtToken(AuthUser authUser) {
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
Date exp = new Date(nowMillis + 3600000); // 1 hour validity
List<String> roles = authUser.roles().stream().map(Role::name).toList();
return JWT.create()
.withSubject(authUser.id())
.withClaim(ROLES_CLAIM, roles)
.withIssuedAt(now)
.withExpiresAt(exp)
.sign(signingAlgorithm);
}
}
JWT Authentication Filter
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtService jwtService;
public JwtAuthenticationFilter(JwtService jwtService) {
this.jwtService = jwtService;
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
String token = header.substring(7);
AuthUser authUser = jwtService.resolveJwtToken(token);
Authentication authentication = new UsernamePasswordAuthenticationToken(
authUser, null, authUser.roles());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
}
Security Configuration
@EnableWebSecurity
@Configuration
public class SecurityConfig {
private final JwtService jwtService;
public SecurityConfig(JwtService jwtService) {
this.jwtService = jwtService;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(
new JwtAuthenticationFilter(jwtService),
UsernamePasswordAuthenticationFilter.class
);
return http.build();
}
}
✅ Best Practices
- Use HTTPS to protect token transmission.
- Keep JWT payload minimal; avoid sensitive data.
- Set reasonable expiration times; consider refresh tokens.
- Stateless JWT works well for microservices.
- For revocation or blacklisting, implement an additional token store.
🧩 Example Requests
Login Request:
POST /login
Body: { "username": "user", "password": "123" }
Login Response:
{ "token": "eyJhbGciOiJIUzI1NiIs..." }
Authenticated Request:
GET /api/user
Header: Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Response:
200 OK
{ "id": "user", "roles": ["USER"] }
🚀 Summary
This guide demonstrates a practical JWT-based authentication implementation in Spring Security. Its stateless design simplifies scaling and microservices integration while maintaining secure access control.