Security with Spring Security + JWT

Module 7 · ~10 min read
JWT (JSON Web Token) authentication is stateless — the server stores no session data. Each request carries a self-contained, signed token. Spring Security validates the token on every request via a custom filter inserted before the standard security chain.

Filter Chain Order

Incoming HTTP request │ ▼ JwtAuthFilter (OncePerRequestFilter) ├── Extract "Authorization: Bearer <token>" header ├── Parse JWT → extract username ├── Load UserDetails from database ├── Validate token (signature, expiry) └── Set Authentication in SecurityContextHolder │ ▼ Spring Security filter chain ├── /api/auth/** → permitAll ├── /actuator/health → permitAll ├── /api/admin/** → hasRole("ADMIN") └── everything else → authenticated │ ▼ Controller

JwtAuthFilter

JwtAuthFilter.java View source ↗
public class JwtAuthFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, ...) {
        String authHeader = request.getHeader("Authorization");
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            filterChain.doFilter(request, response);
            return;
        }
        String jwt = authHeader.substring(7);
        String username = jwtService.extractUsername(jwt);
        if (username != null && SecurityContext has no auth) {
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            if (jwtService.isTokenValid(jwt, userDetails)) {
                // Set authentication in SecurityContext
            }
        }
        filterChain.doFilter(request, response);
    }
}

Key Implementation Points

SecurityConfig — Role-Based Access

SecurityConfig.java — authorization rules View source ↗
.authorizeHttpRequests(auth -> auth
    .requestMatchers("/api/auth/**", "/actuator/health").permitAll()
    .requestMatchers("/api/admin/**").hasRole("ADMIN")
    .anyRequest().authenticated())

Role Hierarchy

RoleCapabilities
USER Ask questions, upload documents to their own workspace, view their own history
ADMIN All USER capabilities + manage all documents, view all users, access admin endpoints
JWT is stateless — no session storage needed. Token expiry is set to 24 hours. For high-security deployments, combine short-lived access tokens (15 min) with a refresh token flow. Power RAG uses a single 24h token for simplicity.

JWT Token Structure

A JWT has three Base64url-encoded parts separated by dots:

The signature cannot be forged without the secret key. Tampering with the payload invalidates the signature and the token is rejected.