0Pricing
Spring Boot 4 Complete Guide · درس

فحص الرموز غير الشفافة وتبادل الرموز

تحقق من الرموز غير الشفافة عبر الفحص الاستقرائي وانقل الهوية باستخدام تبادل الرموز

فحص الرموز غير الشفافة وتبادل الرموز درس مجاني في Spring Boot 4 Complete Guide على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Spring Boot 4 Complete Guide، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Spring Boot 4 Complete Guide 4 دروس في المجموع.

بعض أجزاء هذا الدرس لم تُترجم بعد وتظهر باللغة الإنجليزية.

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.

الأسئلة الشائعة

هل درس «فحص الرموز غير الشفافة وتبادل الرموز» مجاني؟

نعم — نص درس «فحص الرموز غير الشفافة وتبادل الرموز» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Spring Boot 4 Complete Guide، انتقل إلى CoddyKit PRO. تتضمن دورة Spring Boot 4 Complete Guide 4 دروس في المجموع.

ماذا ستتعلم في «فحص الرموز غير الشفافة وتبادل الرموز»؟

تحقق من الرموز غير الشفافة عبر الفحص الاستقرائي وانقل الهوية باستخدام تبادل الرموز تتمرن على Spring Boot 4 Complete Guide مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Spring Boot 4 Complete Guide؟

لا تُشترط خبرة سابقة. Spring Boot 4 Complete Guide على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «فحص الرموز غير الشفافة وتبادل الرموز»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Spring Boot 4 Complete Guide هذا؟

نعم. كل درس في Spring Boot 4 Complete Guide يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. التحقق من JWT ومطالباته في خادم الموارد
  2. عميل OAuth2 وتدفق رمز التفويض
  3. أمان الأساليب باستخدام SpEL والمصوّتين المخصصين
  4. فحص الرموز غير الشفافة وتبادل الرموز
← العودة إلى Spring Boot 4 Complete Guide