Spring Security and JWT
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
@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
Know these first
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.