QTPortfolio
Mastering Spring Security: JWT + httpOnly Cookies
Spring BootSecurityJWTJava

Mastering Spring Security: JWT + httpOnly Cookies

February 10, 20262 min read~226 words

The Problem with localStorage

Many tutorials store JWT tokens in localStorage or sessionStorage. This is a critical security mistake — any JavaScript on your page (including third-party scripts) can read these values, making you vulnerable to XSS attacks.

The httpOnly Cookie Solution

By storing the JWT in an httpOnly cookie, the browser automatically includes it in every request, but JavaScript can never read it. This is the gold standard for web authentication.

Implementing the JWT Provider

@Component
public class JwtTokenProvider {
    
    @Value("${jwt.secret}")
    private String jwtSecret;
    
    public String generateToken(Authentication authentication) {
        return Jwts.builder()
            .subject(authentication.getName())
            .issuedAt(new Date())
            .expiration(new Date(System.currentTimeMillis() + 7 * 24 * 60 * 60 * 1000L))
            .signWith(getSigningKey())
            .compact();
    }
    
    private SecretKey getSigningKey() {
        byte[] keyBytes = Decoders.BASE64.decode(jwtSecret);
        return Keys.hmacShaKeyFor(keyBytes);
    }
}

The Authentication Filter

The filter extracts the JWT from the cookie and sets the security context:

@Component
public class JwtAuthFilter extends OncePerRequestFilter {
    
    @Override
    protected void doFilterInternal(HttpServletRequest request,
            HttpServletResponse response, FilterChain chain) {
        
        String token = Arrays.stream(request.getCookies())
            .filter(c -> "jwt".equals(c.getName()))
            .map(Cookie::getValue)
            .findFirst()
            .orElse(null);
            
        if (token != null && jwtTokenProvider.validateToken(token)) {
            String username = jwtTokenProvider.getUsername(token);
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            UsernamePasswordAuthenticationToken auth = 
                new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(auth);
        }
        chain.doFilter(request, response);
    }
}

Conclusion

Using httpOnly cookies with JWT is more secure than localStorage and doesn't sacrifice developer experience. Combined with proper CSRF protection and HTTPS in production, this pattern is battle-tested and production-ready.

Enjoyed this article?

Share it with your network or explore more posts below.