Spring Boot 4 Complete Guide · Ders

Opak Belirteç İncelemesi ve Belirteç Değişimi

Opak belirteçleri inceleme yoluyla doğrulayın ve belirteç değişimiyle kimlik bilgisini aktarın.

4. ders / 413 adım

Opak Belirteç İncelemesi ve Belirteç Değişimi, CoddyKit'te ücretsiz bir Spring Boot 4 Complete Guide dersidir. Bu, 4 dersinin 4. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, Spring Boot 4 Complete Guide öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. Spring Boot 4 Complete Guide kursu toplamda 4 dersten oluşur.

Bu dersin bazı bölümleri henüz çevrilmemiş olup İngilizce olarak gösterilmektedir.

Why Opaque Tokens Need Introspection

A JWT carries its claims inside the token, so a resource server can validate it offline by checking the signature. An opaque token is just a random reference string — it contains no claims and no signature you can verify.

  • To learn who the token belongs to and whether it is still valid, the resource server must ask the authorization server.
  • That call is the OAuth2 Token Introspection endpoint, defined by RFC 7662.
  • The authorization server replies with active: true/false plus claims like sub, scope, and exp.

Opaque tokens trade offline speed for instant revocation: revoke at the AS and the very next introspection returns active: false.

The Introspection Response

Per RFC 7662, the introspection endpoint accepts the token as a form parameter and returns a JSON document. The single mandatory field is active.

A typical successful response looks like the JSON below. If the token is unknown, expired, or revoked, the server returns simply {"active": false} and Spring rejects the request.

{
  "active": true,
  "sub": "user-123",
  "scope": "orders:read orders:write",
  "client_id": "web-app",
  "username": "alice",
  "token_type": "Bearer",
  "exp": 1735689600,
  "iat": 1735686000
}

Configuring Opaque Token Validation

In a Spring Boot 4 resource server you enable opaque-token validation with a single properties block. Spring Security autoconfigures an OpaqueTokenIntrospector from these values.

  • introspection-uri — the AS endpoint that implements RFC 7662.
  • client-id / client-secret — the resource server's own credentials to authenticate the introspection call.
# application.yml
spring:
  security:
    oauth2:
      resourceserver:
        opaque-token:
          introspection-uri: https://auth.example.com/oauth2/introspect
          client-id: resource-server
          client-secret: ${INTROSPECT_SECRET}

Enabling It in the Security Filter Chain

The properties only take effect once you opt into opaqueToken() on the resource server DSL. This wires the autoconfigured introspector into the bearer-token filter.

Notice the contrast with JWT mode: with opaque tokens there is no local key set — every request triggers a network call to the AS.

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/**").authenticated()
                .anyRequest().permitAll())
            .oauth2ResourceServer(oauth2 -> oauth2
                .opaqueToken(Customizer.withDefaults()));
        return http.build();
    }
}

A Custom OpaqueTokenIntrospector

You can define your own OpaqueTokenIntrospector bean to control the HTTP client, caching, or response parsing. The built-in SpringOpaqueTokenIntrospector uses a RestClient under the hood.

Returning a bean overrides the autoconfigured one, so you keep the same opaqueToken() DSL while customizing behavior.

@Bean
OpaqueTokenIntrospector introspector(
        @Value("${spring.security.oauth2.resourceserver.opaque-token.introspection-uri}") String uri,
        @Value("${spring.security.oauth2.resourceserver.opaque-token.client-id}") String clientId,
        @Value("${spring.security.oauth2.resourceserver.opaque-token.client-secret}") String secret) {

    RestClient restClient = RestClient.builder()
        .requestInterceptor(new BasicAuthenticationInterceptor(clientId, secret))
        .build();

    return new SpringOpaqueTokenIntrospector(uri, restClient);
}

Mapping Scopes to Authorities

By default the introspected scope string becomes a set of SCOPE_ authorities. To customize that mapping — for example adding role authorities — wrap the default introspector and transform its principal.

The OAuth2AuthenticatedPrincipal exposes the raw introspection claims via getAttributes(), which you remap into your own authorities.

public class CustomAuthoritiesIntrospector implements OpaqueTokenIntrospector {

    private final OpaqueTokenIntrospector delegate;

    public CustomAuthoritiesIntrospector(OpaqueTokenIntrospector delegate) {
        this.delegate = delegate;
    }

    @Override
    public OAuth2AuthenticatedPrincipal introspect(String token) {
        OAuth2AuthenticatedPrincipal principal = delegate.introspect(token);
        List<GrantedAuthority> authorities = new ArrayList<>(principal.getAuthorities());
        String scope = principal.getAttribute("scope");
        if (scope != null && scope.contains("orders:write")) {
            authorities.add(new SimpleGrantedAuthority("ROLE_ORDER_MANAGER"));
        }
        return new DefaultOAuth2AuthenticatedPrincipal(
            principal.getName(), principal.getAttributes(), authorities);
    }
}

Caching Introspection Results

Because every request hits the AS, opaque tokens can become a latency and load bottleneck. A safe optimization is to cache the introspection result for a short window — never longer than the token's remaining lifetime.

  • Cache on the token value as the key.
  • Honor exp: an entry must expire at or before the token's own expiry.
  • Keep the TTL small (seconds to a minute) so revocation stays nearly immediate.

This small helper shows the core TTL math you would apply inside a caching introspector.

import java.time.Instant;

public class IntrospectionCache {

    public static long ttlSeconds(long tokenExpEpoch, long maxCacheSeconds) {
        long now = Instant.now().getEpochSecond();
        long remaining = tokenExpEpoch - now;
        if (remaining <= 0) {
            return 0; // already expired, do not cache
        }
        return Math.min(remaining, maxCacheSeconds);
    }

    public static void main(String[] args) {
        long exp = Instant.now().getEpochSecond() + 300; // expires in 5 min
        System.out.println("Cache for " + ttlSeconds(exp, 30) + "s");
        System.out.println("Cache for " + ttlSeconds(Instant.now().getEpochSecond() - 10, 30) + "s");
    }
}

The Identity Propagation Problem

Now suppose your API gateway received a token for the user, and it must call a downstream service. Forwarding the original token leaks audience and over-grants scope. Re-using the gateway's own client credentials loses the user's identity entirely.

OAuth2 Token Exchange (RFC 8693) solves this: the gateway presents the incoming token to the AS and asks for a new token scoped to the downstream service, while preserving the original subject.

  • Identity is propagated, not impersonated blindly.
  • The new token's aud targets exactly the downstream service.
  • Scopes can be narrowed for least privilege.

The Token Exchange Request

A token exchange is a POST to the AS token endpoint with grant_type=urn:ietf:params:oauth:grant-type:token-exchange. The key parameters are the subject_token (the incoming token) and its subject_token_type.

  • subject_token — the token representing the user.
  • requested_token_type — usually an access token.
  • audience or resource — the downstream service the new token is for.
POST /oauth2/token HTTP/1.1
Host: auth.example.com
Authorization: Basic <gateway-client-credentials>
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=<incoming-user-token>
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&requested_token_type=urn:ietf:params:oauth:token-type:access_token
&audience=orders-service

Token Exchange in Spring Security 6.3+

Spring Security ships a TokenExchangeOAuth2AuthorizedClientProvider so a resource server can exchange the current token before calling downstream. Register the provider and a matching client registration with the token-exchange grant type.

The client registration uses authorization-grant-type set to the RFC 8693 URN.

@Bean
OAuth2AuthorizedClientManager authorizedClientManager(
        ClientRegistrationRepository registrations,
        OAuth2AuthorizedClientRepository clients) {

    OAuth2AuthorizedClientProvider provider =
        OAuth2AuthorizedClientProviderBuilder.builder()
            .provider(new TokenExchangeOAuth2AuthorizedClientProvider())
            .build();

    DefaultOAuth2AuthorizedClientManager manager =
        new DefaultOAuth2AuthorizedClientManager(registrations, clients);
    manager.setAuthorizedClientProvider(provider);
    return manager;
}

Calling Downstream With the Exchanged Token

With the manager in place, configure a RestClient (or WebClient) that attaches the exchanged token automatically. The interceptor resolves the authorized client named orders-service, triggering the exchange when needed.

The downstream service then validates a token whose sub is still the original user and whose aud is itself — clean, least-privilege identity propagation.

@Bean
RestClient ordersRestClient(OAuth2AuthorizedClientManager manager) {
    OAuth2ClientHttpRequestInterceptor interceptor =
        new OAuth2ClientHttpRequestInterceptor(manager);
    interceptor.setClientRegistrationIdResolver(request -> "orders-service");

    return RestClient.builder()
        .baseUrl("https://orders.internal")
        .requestInterceptor(interceptor)
        .build();
}

Quick Check: Choosing the Right Mechanism

Test your understanding of when to use introspection versus token exchange.

Recap

You learned how to validate reference tokens and propagate identity across services:

  • Opaque tokens carry no claims, so the resource server calls the AS introspection endpoint (RFC 7662); the only required field is active.
  • Enable it with the opaque-token properties plus oauth2ResourceServer(o -> o.opaqueToken(...)); customize via a custom OpaqueTokenIntrospector.
  • Map scopes to authorities and cache results briefly — never beyond the token's exp — to limit AS load while keeping revocation fast.
  • Token exchange (RFC 8693) swaps an incoming token for a downstream-scoped one, preserving the original sub while setting the correct aud.
  • In Spring, the TokenExchangeOAuth2AuthorizedClientProvider plus an authorized-client interceptor makes this automatic for downstream calls.
Başlamak ücretsiz

Yapay zeka eğitmeniyle Java öğren — ücretsiz

Tarayıcında gerçek kod yaz ve çalıştır, 7/24 yapay zeka eğitmeninden anında yardım al; web'de ya da uygulamada kaldığın yerden devam et.

Kurslar
21
Dersler
84

Sıkça Sorulan Sorular

“Opak Belirteç İncelemesi ve Belirteç Değişimi” dersi ücretsiz mi?

Evet — “Opak Belirteç İncelemesi ve Belirteç Değişimi” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve Spring Boot 4 Complete Guide kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. Spring Boot 4 Complete Guide kursu toplamda 4 dersten oluşur.

“Opak Belirteç İncelemesi ve Belirteç Değişimi” dersinde ne öğreneceğim?

Opak belirteçleri inceleme yoluyla doğrulayın ve belirteç değişimiyle kimlik bilgisini aktarın. Spring Boot 4 Complete Guide ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.

Spring Boot 4 Complete Guide öğrenmeye başlamak için deneyim gerekli mi?

Önceden deneyim gerekmez. CoddyKit'te Spring Boot 4 Complete Guide, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 4. dersidir.

“Opak Belirteç İncelemesi ve Belirteç Değişimi” dersi ne kadar sürer?

Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.

Bu Spring Boot 4 Complete Guide dersinde kod yazıp çalıştırabilir miyim?

Evet. Her Spring Boot 4 Complete Guide dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.

Bu kursun tüm dersleri

  1. Kaynak Sunucusu JWT Doğrulaması ve Talepler
  2. OAuth2 İstemcisi ve Yetkilendirme Kodu Akışı
  3. SpEL ve Özel Oylayıcılarla Metot Güvenliği
  4. Opak Belirteç İncelemesi ve Belirteç Değişimi
← Spring Boot 4 Complete Guide Sayfasına Dön