Stage 11: Spring Boot, lesson 5 of 8

Spring Security and JWT

Advanced3 min read@since 17Code runs on your Java 25
Explain it forThe essentials plus production detail and pitfalls.

Spring Security is a chain of servlet filters in front of your application.

  • Authentication verifies identity: username and password, OAuth2 login, or a JWT.
  • Authorization checks permissions per URL (requestMatchers) or per method (@PreAuthorize).
  • Passwords must always be hashed with BCryptPasswordEncoder (or Argon2), never stored as plain text.

For stateless REST APIs, the client sends Authorization: Bearer <token>. Configure the app as an OAuth2 resource server, which checks the token's signature, expiry and claims. Keycloak, Auth0 or Spring Authorization Server can issue the tokens.

This matches how a learning site like this one works: browsing is public (permitAll), and only checkout and my-courses endpoints need a token.

Example

Java
@Configuration
@EnableMethodSecurity
public class SecurityConfig {

    @Bean
    SecurityFilterChain api(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())                 // stateless API using bearer tokens
            .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authorizeHttpRequests(auth -> auth
                .requestMatchers(HttpMethod.GET, "/api/v1/lessons/**", "/api/v1/courses/**").permitAll()
                .requestMatchers("/api/v1/checkout/**", "/api/v1/me/**").authenticated()
                .requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
                .anyRequest().denyAll())
            .oauth2ResourceServer(o -> o.jwt(Customizer.withDefaults()));
        return http.build();
    }

    @Bean
    PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
}

@PreAuthorize("hasRole('ADMIN') or #userId == authentication.name")
public Invoice invoice(String userId, Long orderId) { /* ... */ return null; }

Common mistake

Storing JWTs in localStorage, where any XSS bug can read them. For browser apps prefer HttpOnly, Secure, SameSite cookies, and then keep CSRF protection on.

Under the hood

Important filters, in order: CORS, CSRF, authentication (for example BearerTokenAuthenticationFilter), ExceptionTranslationFilter, then AuthorizationFilter. The authenticated user lives in the SecurityContextHolder. A JWT can't be revoked before it expires, so keep access tokens short-lived (5 to 15 minutes) with refresh tokens, and never put secrets in the payload: it's only Base64-encoded, not encrypted.

Check yourself

Which should you use to store passwords?

How this connects

Where this leads

You've reached the end of this thread. Try a learning path for what's next.

Part of Job-ready backend developer, Crack the Java interview, Microservices and production.

Was this lesson helpful?

Finished reading? Mark it complete to track your progress.