Careeroza
Configuring a Resource Server
Dependency and Properties
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>application.yml
Point Spring at the issuer. On startup it reads the discovery document and the JWKS keys:
order-service/src/main/resources/application.yml
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: ${OAUTH_ISSUER:http://localhost:8180/realms/shopease}
audiences: shopease-apiTip: If the API must start even when the authorization server is down, set jwk-set-uri as well; Spring then fetches keys lazily on the first request instead of calling discovery at startup.
Security Configuration
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class ResourceServerConfig {
@Bean
SecurityFilterChain api(HttpSecurity http, JwtAuthenticationConverter jwtAuthConverter) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health").permitAll()
.requestMatchers(HttpMethod.GET, "/api/orders/**").hasAuthority("SCOPE_orders.read")
.requestMatchers(HttpMethod.POST, "/api/orders/**").hasAuthority("SCOPE_orders.write")
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated())
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthConverter)))
.sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.csrf(csrf -> csrf.disable()); // no cookies: bearer tokens are not sent automatically
return http.build();
}
}@RestController
@RequestMapping("/api/orders")
public class OrderController {
private final OrderService orders;
public OrderController(OrderService orders) { this.orders = orders; }
@GetMapping
public List<OrderDto> myOrders(@AuthenticationPrincipal Jwt jwt) {
return orders.findByCustomer(jwt.getSubject()); // "sub" claim: the user's stable id
}
}What the Client Sees
Requests
No token, an expired token, a token without the needed scope, and a good token:
curl -i http://localhost:8081/api/orders
curl -i http://localhost:8081/api/orders -H "Authorization: Bearer $EXPIRED"
curl -i -X POST http://localhost:8081/api/orders -H "Authorization: Bearer $READ_ONLY" -H "Content-Type: application/json" -d '{"items":[{"productId":7,"quantity":1}]}'
curl -i http://localhost:8081/api/orders -H "Authorization: Bearer $TOKEN"Example output
HTTP/1.1 401
WWW-Authenticate: Bearer
HTTP/1.1 401
WWW-Authenticate: Bearer error="invalid_token", error_description="An error occurred while attempting to decode the Jwt: Jwt expired at 2026-03-01T04:45:00Z", error_uri="https://tools.ietf.org/html/rfc6750#section-3.1"
HTTP/1.1 403
WWW-Authenticate: Bearer error="insufficient_scope", error_description="The request requires higher privileges than provided by the access token.", error_uri="https://tools.ietf.org/html/rfc6750#section-3.1"
HTTP/1.1 200
Content-Type: application/json
[{"id":1042,"status":"SHIPPED","total":2499.00,"placedAt":"2026-02-27T18:22:05"}]