Secure a stateless API with JSON Web Tokens, validate them as an OAuth2 resource server, and let users log in through an external provider with OAuth2 login.
Why: HTTP is stateless and REST APIs usually have no session — instead the client sends a signed token on every request in the Authorization header. A JWT (JSON Web Token) carries the user’s identity and roles, signed so the server can trust it without a database lookup.
# the client sends the token on every request
curl http://localhost:8080/api/books \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsIn..."Why: the OAuth2 resource server support validates the token’s signature and expiry for you — you just configure where to find the signing key. Note: add spring-boot-starter-oauth2-resource-server and point it at the issuer (or a symmetric secret) in properties.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(a -> a.anyRequest().authenticated())
.oauth2ResourceServer(oauth -> oauth.jwt(Customizer.withDefaults()));
return http.build();
}# application.yml — where to fetch the public keys that verify tokens
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://auth.example.com/When you mint your own tokens: a login endpoint checks the password, then encodes a signed JWT with the user’s identity and an expiry. Note: use a JwtEncoder bean; keep the signing secret in an environment variable, never in code.
@Service
public class TokenService {
private final JwtEncoder encoder;
public TokenService(JwtEncoder encoder) { this.encoder = encoder; }
public String createToken(Authentication auth) {
Instant now = Instant.now();
JwtClaimsSet claims = JwtClaimsSet.builder()
.issuer("bookstore")
.issuedAt(now)
.expiresAt(now.plus(1, ChronoUnit.HOURS))
.subject(auth.getName())
.claim("roles", auth.getAuthorities().toString())
.build();
return encoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
}
}When OAuth2 login: you want "Sign in with Google/GitHub" instead of managing passwords. Note: add spring-boot-starter-oauth2-client, register the provider’s client id and secret, and Spring handles the whole redirect-and-callback dance with .oauth2Login().
# application.yml
spring:
security:
oauth2:
client:
registration:
github:
client-id: ${GITHUB_CLIENT_ID}
client-secret: ${GITHUB_CLIENT_SECRET}
scope: read:user
# then in the filter chain: http.oauth2Login(Customizer.withDefaults());