or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

aot-native-support.mdauthentication-core.mdauthentication-events.mdauthentication-management.mdauthentication-tokens.mdauthorities.mdauthorization.mdcompromised-password.mdconcurrent-async.mddao-authentication.mdexpression-access-control.mdindex.mdjaas-authentication.mdjackson-serialization.mdmethod-security.mdobservation-metrics.mdone-time-tokens.mdprovisioning.mdsecurity-context.mdsession-management.mduser-details.md
tile.json

security-context.mddocs/

Security Context Management

Thread-local and reactive context management for associating security information with the current execution context. Includes the SecurityContext interface, SecurityContextHolder, strategies for different threading models, and reactive support.

Key Information for Agents

Core Capabilities:

  • SecurityContext stores current Authentication object
  • SecurityContextHolder provides static access to thread-local security context
  • Multiple storage strategies: ThreadLocal (default), InheritableThreadLocal, Global, Listening
  • Reactive support via ReactiveSecurityContextHolder using Project Reactor's Context
  • Automatic context propagation via delegating executors and schedulers

Key Interfaces and Classes:

  • SecurityContext - Interface storing Authentication (get/set methods)
  • SecurityContextImpl - Default implementation
  • TransientSecurityContext - Context that should not be persisted (e.g., not saved in session)
  • SecurityContextHolder - Static utility for accessing context (uses strategy pattern)
  • SecurityContextHolderStrategy - Strategy interface for context storage
  • ThreadLocalSecurityContextHolderStrategy - Default strategy (thread-bound)
  • InheritableThreadLocalSecurityContextHolderStrategy - Inherited by child threads
  • GlobalSecurityContextHolderStrategy - Single global context (rarely used)
  • ListeningSecurityContextHolderStrategy - Publishes context change events
  • ReactiveSecurityContextHolder - Reactive context holder using Reactor Context

Default Behaviors:

  • Default strategy: MODE_THREADLOCAL (ThreadLocal-based, thread-bound)
  • getContext() creates empty context if none exists (never returns null)
  • setContext(null) effectively clears context (but use clearContext() instead)
  • createEmptyContext() creates new SecurityContextImpl instance
  • Strategy can be set via system property: spring.security.strategy
  • Reactive context: Stored in Reactor's Context, accessed via getContext() returning Mono<SecurityContext>

Threading Model:

  • ThreadLocal strategy: Each thread has independent context (default)
  • InheritableThreadLocal: Child threads inherit parent's context automatically
  • Global strategy: All threads share same context (not thread-safe for modifications)
  • Reactive: Context stored in Reactor Context, propagates through reactive chains automatically
  • Context propagation: Use delegating executors (DelegatingSecurityContextExecutor, etc.) for async operations

Lifecycle:

  • Context created on first access via getContext()
  • Context cleared on logout or explicit clearContext() call
  • Transient contexts not persisted to session
  • Deferred context loading: Supports lazy loading via Supplier<SecurityContext> (since 5.8)

Exceptions:

  • No exceptions thrown by context holder (returns empty context if none exists)
  • Context change events: SecurityContextChangedEvent published by listening strategy
  • Event contains old/new context and helper methods: isCleared(), wasAuthenticated(), isAuthenticated()

Edge Cases:

  • Null authentication: getAuthentication() returns null if not authenticated (valid state)
  • Empty context: getContext() never returns null, but may return context with null authentication
  • Thread pool reuse: Must clear context in pooled threads (use try/finally or delegating executors)
  • Async execution: Context not automatically propagated - use delegating executors
  • Reactive chains: Context propagates automatically via ReactiveSecurityContextHolder
  • Session storage: Transient contexts not saved to HTTP session
  • Context snapshot: Use ContextSnapshot for capturing context across thread boundaries (Project Reactor)

Core Interfaces

SecurityContext

Interface for storing the current Authentication object.

package org.springframework.security.core.context;

interface SecurityContext extends Serializable {
    Authentication getAuthentication();
    void setAuthentication(Authentication authentication);
}

Method Details:

  • getAuthentication() - Returns the currently authenticated principal, or null if not authenticated.
  • setAuthentication(Authentication) - Changes the currently authenticated principal, or removes authentication if null.

SecurityContextImpl

Default implementation of SecurityContext.

package org.springframework.security.core.context;

class SecurityContextImpl implements SecurityContext {
    SecurityContextImpl();
    SecurityContextImpl(Authentication authentication);

    Authentication getAuthentication();
    void setAuthentication(Authentication authentication);
    boolean equals(Object obj);
    int hashCode();
    String toString();
}

Constructor Details:

  • No-arg constructor creates empty context.
  • Single-arg constructor initializes with authentication.

Usage Example:

// Create security context
SecurityContext context = new SecurityContextImpl();
Authentication auth = UsernamePasswordAuthenticationToken.authenticated(
    "user", null, AuthorityUtils.createAuthorityList("ROLE_USER"));
context.setAuthentication(auth);

// Or with constructor
SecurityContext context = new SecurityContextImpl(auth);

TransientSecurityContext

Security context that should not be persisted (e.g., not saved in session).

package org.springframework.security.core.context;

class TransientSecurityContext implements SecurityContext {
    TransientSecurityContext();
    TransientSecurityContext(Authentication authentication);

    Authentication getAuthentication();
    void setAuthentication(Authentication authentication);
    boolean equals(Object obj);
    int hashCode();
    String toString();
}

Usage Example:

// For temporary authentication that shouldn't be persisted
Authentication tempAuth = // ... temporary authentication
SecurityContext transientContext = new TransientSecurityContext(tempAuth);

// This context will not be saved to HTTP session
SecurityContextHolder.setContext(transientContext);

SecurityContextHolder

Thread-local storage for SecurityContext providing convenient static access.

package org.springframework.security.core.context;

class SecurityContextHolder {
    static final String MODE_THREADLOCAL = "MODE_THREADLOCAL";
    static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL";
    static final String MODE_GLOBAL = "MODE_GLOBAL";
    static final String SYSTEM_PROPERTY = "spring.security.strategy";

    static void clearContext();
    static SecurityContext getContext();
    static void setContext(SecurityContext context);
    static SecurityContext createEmptyContext();
    static void setStrategyName(String strategyName);
    static void setContextHolderStrategy(SecurityContextHolderStrategy strategy);
    static SecurityContextHolderStrategy getContextHolderStrategy();
    static Supplier<SecurityContext> getDeferredContext();
    static String toString();
}

Constants:

  • MODE_THREADLOCAL - Uses ThreadLocal (default, thread-bound).
  • MODE_INHERITABLETHREADLOCAL - Uses InheritableThreadLocal (inherited by child threads).
  • MODE_GLOBAL - Single global context (rarely used).
  • SYSTEM_PROPERTY - System property name for setting strategy: "spring.security.strategy".

Method Details:

  • clearContext() - Clears the current security context.
  • getContext() - Returns the current context (creates empty one if none exists).
  • setContext(SecurityContext) - Sets the current context.
  • createEmptyContext() - Creates a new empty context instance.
  • setStrategyName(String) - Sets the strategy by name (MODE_THREADLOCAL, etc.).
  • setContextHolderStrategy(SecurityContextHolderStrategy) - Sets custom strategy.
  • getContextHolderStrategy() - Returns the current strategy.
  • getDeferredContext() - Gets deferred context supplier for lazy loading.

Usage Example:

// Get current authentication (null-safe)
SecurityContext context = SecurityContextHolder.getContext();
Authentication auth = context != null ? context.getAuthentication() : null;
if (auth != null && auth.isAuthenticated()) {
    String username = auth.getName();
    Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();
    Object principal = auth.getPrincipal(); // UserDetails or String
} else {
    // No authentication - anonymous or unauthenticated
    log.debug("No authenticated user in security context");
}

// Set authentication
Authentication newAuth = UsernamePasswordAuthenticationToken.authenticated(
    "user", null, AuthorityUtils.createAuthorityList("ROLE_USER"));
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(newAuth);
SecurityContextHolder.setContext(context);

// Clear context (e.g., on logout)
SecurityContextHolder.clearContext();

// Check if user has role
boolean hasAdminRole = SecurityContextHolder.getContext()
    .getAuthentication()
    .getAuthorities().stream()
    .anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"));

// Change strategy (before application initialization)
SecurityContextHolder.setStrategyName(
    SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);

SecurityContextHolderStrategy

Strategy interface for storing SecurityContext.

package org.springframework.security.core.context;

interface SecurityContextHolderStrategy {
    void clearContext();
    SecurityContext getContext();
    void setContext(SecurityContext context);
    SecurityContext createEmptyContext();
    default Supplier<SecurityContext> getDeferredContext();
    default void setDeferredContext(Supplier<SecurityContext> deferredContext);
}

Method Details:

  • clearContext() - Clears the stored context.
  • getContext() - Obtains the current context.
  • setContext(SecurityContext) - Sets a new context.
  • createEmptyContext() - Creates new empty context (should not use SecurityContextImpl directly to allow subclassing).
  • getDeferredContext() - Gets supplier for deferred context loading (since 5.8).
  • setDeferredContext(Supplier) - Sets deferred context (since 5.8).

ThreadLocalSecurityContextHolderStrategy

Default strategy using ThreadLocal.

package org.springframework.security.core.context;

class ThreadLocalSecurityContextHolderStrategy
        implements SecurityContextHolderStrategy {

    ThreadLocalSecurityContextHolderStrategy();

    void clearContext();
    SecurityContext getContext();
    void setContext(SecurityContext context);
    SecurityContext createEmptyContext();
    Supplier<SecurityContext> getDeferredContext();
    void setDeferredContext(Supplier<SecurityContext> deferredContext);
}

Usage Example:

// Each thread has its own security context
SecurityContextHolderStrategy strategy =
    new ThreadLocalSecurityContextHolderStrategy();

// In thread 1
strategy.setContext(context1);

// In thread 2
strategy.setContext(context2);

// Each thread sees its own context
SecurityContext retrieved = strategy.getContext(); // context for current thread

InheritableThreadLocalSecurityContextHolderStrategy

Strategy that allows child threads to inherit parent's context.

package org.springframework.security.core.context;

class InheritableThreadLocalSecurityContextHolderStrategy
        implements SecurityContextHolderStrategy {

    InheritableThreadLocalSecurityContextHolderStrategy();

    void clearContext();
    SecurityContext getContext();
    void setContext(SecurityContext context);
    SecurityContext createEmptyContext();
}

Usage Example:

SecurityContextHolderStrategy strategy =
    new InheritableThreadLocalSecurityContextHolderStrategy();
SecurityContextHolder.setContextHolderStrategy(strategy);

// Set context in parent thread
Authentication auth = // ... authentication
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(auth);
SecurityContextHolder.setContext(context);

// Child threads inherit the context
new Thread(() -> {
    // This thread automatically has the parent's security context
    Authentication inherited =
        SecurityContextHolder.getContext().getAuthentication();
    System.out.println("Inherited user: " + inherited.getName());
}).start();

GlobalSecurityContextHolderStrategy

Strategy using single static instance shared across all threads.

package org.springframework.security.core.context;

class GlobalSecurityContextHolderStrategy
        implements SecurityContextHolderStrategy {

    GlobalSecurityContextHolderStrategy();

    void clearContext();
    SecurityContext getContext();
    void setContext(SecurityContext context);
    SecurityContext createEmptyContext();
}

Usage Example:

// Rarely used - all threads share same context
SecurityContextHolderStrategy strategy = new GlobalSecurityContextHolderStrategy();
SecurityContextHolder.setContextHolderStrategy(strategy);

// All threads see the same context (not thread-safe for modifications)

ListeningSecurityContextHolderStrategy

Strategy that publishes context change events.

package org.springframework.security.core.context;

class ListeningSecurityContextHolderStrategy
        implements SecurityContextHolderStrategy {

    ListeningSecurityContextHolderStrategy(
        SecurityContextHolderStrategy delegate,
        SecurityContextChangedListener... listeners);
    ListeningSecurityContextHolderStrategy(
        SecurityContextHolderStrategy delegate,
        Collection<SecurityContextChangedListener> listeners);

    void clearContext();
    SecurityContext getContext();
    void setContext(SecurityContext context);
    SecurityContext createEmptyContext();
    Supplier<SecurityContext> getDeferredContext();
    void setDeferredContext(Supplier<SecurityContext> deferredContext);
}

Constructor Details:

  • Takes delegate strategy and listeners to be notified of context changes.

Usage Example:

// Create custom listener
SecurityContextChangedListener listener = event -> {
    log.info("Security context changed from {} to {}",
        event.getOldContext(), event.getNewContext());
};

// Create listening strategy
SecurityContextHolderStrategy delegate =
    new ThreadLocalSecurityContextHolderStrategy();
ListeningSecurityContextHolderStrategy strategy =
    new ListeningSecurityContextHolderStrategy(delegate, listener);

SecurityContextHolder.setContextHolderStrategy(strategy);

// Now all context changes will trigger listener
SecurityContextHolder.setContext(newContext);

Context Change Listeners

SecurityContextChangedListener

Listener for security context changes.

package org.springframework.security.core.context;

interface SecurityContextChangedListener {
    void securityContextChanged(SecurityContextChangedEvent event);
}

SecurityContextChangedEvent

Event published when security context changes.

package org.springframework.security.core.context;

class SecurityContextChangedEvent {
    SecurityContextChangedEvent(SecurityContext oldContext,
        SecurityContext newContext);

    SecurityContext getOldContext();
    SecurityContext getNewContext();
    boolean isCleared();
    boolean wasAuthenticated();
    boolean isAuthenticated();
}

Method Details:

  • getOldContext() - Returns the previous context.
  • getNewContext() - Returns the new context.
  • isCleared() - Returns true if context was cleared.
  • wasAuthenticated() - Returns true if old context had authentication.
  • isAuthenticated() - Returns true if new context has authentication.

Usage Example:

SecurityContextChangedListener listener = event -> {
    if (event.isCleared()) {
        log.info("Context cleared");
    } else if (!event.wasAuthenticated() && event.isAuthenticated()) {
        log.info("User logged in");
    } else if (event.wasAuthenticated() && !event.isAuthenticated()) {
        log.info("User logged out");
    } else {
        log.info("Context changed");
    }
};

ObservationSecurityContextChangedListener

Publishes Micrometer observations for context changes.

package org.springframework.security.core.context;

class ObservationSecurityContextChangedListener
        implements SecurityContextChangedListener {

    ObservationSecurityContextChangedListener(ObservationRegistry registry);

    void securityContextChanged(SecurityContextChangedEvent event);
}

Usage Example:

ObservationRegistry observationRegistry = ObservationRegistry.create();
ObservationSecurityContextChangedListener listener =
    new ObservationSecurityContextChangedListener(observationRegistry);

SecurityContextHolderStrategy strategy =
    new ListeningSecurityContextHolderStrategy(
        new ThreadLocalSecurityContextHolderStrategy(),
        listener
    );
SecurityContextHolder.setContextHolderStrategy(strategy);

// All context changes now create observations

Deferred Context Loading

DeferredSecurityContext

Supplier for lazy loading of security context.

package org.springframework.security.core.context;

interface DeferredSecurityContext extends Supplier<SecurityContext> {
    SecurityContext get();
    boolean isGenerated();
}

Method Details:

  • get() - Returns the security context, loading it if necessary.
  • isGenerated() - Returns true if get() would generate a new context rather than load existing one.

Usage Example:

// Used internally by Spring Security for lazy context loading
DeferredSecurityContext deferredContext = () -> {
    // Load from session, database, etc.
    return loadSecurityContextFromStorage();
};

SecurityContextHolder.getContextHolderStrategy()
    .setDeferredContext(deferredContext);

// Context loaded on first access
SecurityContext context = SecurityContextHolder.getContext();

Reactive Security Context

ReactiveSecurityContextHolder

Reactive context holder using Project Reactor's Context.

package org.springframework.security.core.context;

class ReactiveSecurityContextHolder {
    static final Class<?> SECURITY_CONTEXT_KEY = SecurityContext.class;

    static Mono<SecurityContext> getContext();
    static Function<Context, Context> clearContext();
    static Context withSecurityContext(Mono<? extends SecurityContext> securityContext);
    static Context withAuthentication(Authentication authentication);
}

Method Details:

  • getContext() - Returns Mono emitting current security context from reactive context.
  • clearContext() - Returns function that clears security context from reactive context.
  • withSecurityContext(Mono) - Creates reactive context with security context.
  • withAuthentication(Authentication) - Creates reactive context with authentication.

Usage Example:

// Get current authentication reactively
Mono<String> username = ReactiveSecurityContextHolder.getContext()
    .map(SecurityContext::getAuthentication)
    .map(Authentication::getName);

// Set authentication in reactive chain
Authentication auth = UsernamePasswordAuthenticationToken.authenticated(
    "user", null, AuthorityUtils.createAuthorityList("ROLE_USER"));

Mono<String> result = Mono.just("data")
    .contextWrite(ReactiveSecurityContextHolder.withAuthentication(auth));

// Or with security context
Mono<SecurityContext> contextMono = Mono.just(securityContext);
Mono<String> result = service.getData()
    .contextWrite(ReactiveSecurityContextHolder.withSecurityContext(contextMono));

// Clear context
Mono<Void> cleared = Mono.empty()
    .contextWrite(ReactiveSecurityContextHolder.clearContext());

ThreadLocalAccessor Integration

SecurityContextHolderThreadLocalAccessor

Integrates SecurityContextHolder with Project Reactor's ContextSnapshot.

package org.springframework.security.core.context;

class SecurityContextHolderThreadLocalAccessor
        implements ThreadLocalAccessor<SecurityContext> {

    static final String KEY = SecurityContext.class.getName();

    SecurityContextHolderThreadLocalAccessor();

    Object key();
    SecurityContext getValue();
    void setValue(SecurityContext value);
    void reset();
}

Usage Example:

// Automatically bridges ThreadLocal and reactive Context
// Usually configured via AutoConfiguration

// Manual configuration if needed
ContextRegistry.getInstance()
    .registerThreadLocalAccessor(new SecurityContextHolderThreadLocalAccessor());

ReactiveSecurityContextHolderThreadLocalAccessor

Accessor for reactive security context holder.

package org.springframework.security.core.context;

class ReactiveSecurityContextHolderThreadLocalAccessor
        implements ThreadLocalAccessor<SecurityContext> {

    ReactiveSecurityContextHolderThreadLocalAccessor();

    Object key();
    SecurityContext getValue();
    void setValue(SecurityContext value);
    void reset();
}

Concurrent Execution

DelegatingSecurityContextExecutor

Executor that propagates SecurityContext to executed tasks.

package org.springframework.security.concurrent;

class DelegatingSecurityContextExecutor implements Executor {
    DelegatingSecurityContextExecutor(Executor delegate);
    DelegatingSecurityContextExecutor(Executor delegate,
        SecurityContext securityContext);

    void execute(Runnable task);
    protected Runnable createRunnable(Runnable originalRunnable);
}

Constructor Details:

  • Takes delegate executor and optional security context (uses current if not provided).

Usage Example:

// Wrap existing executor
ExecutorService executorService = Executors.newFixedThreadPool(10);
Executor secureExecutor = new DelegatingSecurityContextExecutor(executorService);

// Tasks execute with current security context
secureExecutor.execute(() -> {
    // This code has access to the security context from submitting thread
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    log.info("Task running as: {}", auth.getName());
});

DelegatingSecurityContextExecutorService

ExecutorService wrapper with security context propagation.

package org.springframework.security.concurrent;

class DelegatingSecurityContextExecutorService
        extends DelegatingSecurityContextExecutor implements ExecutorService {

    DelegatingSecurityContextExecutorService(ExecutorService delegate);
    DelegatingSecurityContextExecutorService(ExecutorService delegate,
        SecurityContext securityContext);

    void execute(Runnable command);
    <T> Future<T> submit(Callable<T> task);
    <T> Future<T> submit(Runnable task, T result);
    Future<?> submit(Runnable task);
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
        throws InterruptedException;
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
        long timeout, TimeUnit unit) throws InterruptedException;
    <T> T invokeAny(Collection<? extends Callable<T>> tasks)
        throws InterruptedException, ExecutionException;
    <T> T invokeAny(Collection<? extends Callable<T>> tasks,
        long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
    void shutdown();
    List<Runnable> shutdownNow();
    boolean isShutdown();
    boolean isTerminated();
    boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException;
}

Usage Example:

ExecutorService delegate = Executors.newCachedThreadPool();
ExecutorService secureExecutor =
    new DelegatingSecurityContextExecutorService(delegate);

// Submit callable with context propagation
Future<String> future = secureExecutor.submit(() -> {
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    return "Result for user: " + auth.getName();
});

String result = future.get();
log.info(result);

// Shutdown
secureExecutor.shutdown();
secureExecutor.awaitTermination(10, TimeUnit.SECONDS);

DelegatingSecurityContextRunnable

Wraps Runnable to propagate security context.

package org.springframework.security.concurrent;

class DelegatingSecurityContextRunnable implements Runnable {
    DelegatingSecurityContextRunnable(Runnable delegate);
    DelegatingSecurityContextRunnable(Runnable delegate,
        SecurityContext securityContext);

    void run();
}

Usage Example:

Runnable task = () -> {
    log.info("Task logic");
};

// Wrap with current security context
Runnable secureTask = new DelegatingSecurityContextRunnable(task);

// Execute in new thread - context is propagated
new Thread(secureTask).start();

DelegatingSecurityContextCallable

Wraps Callable to propagate security context.

package org.springframework.security.concurrent;

class DelegatingSecurityContextCallable<V> implements Callable<V> {
    DelegatingSecurityContextCallable(Callable<V> delegate);
    DelegatingSecurityContextCallable(Callable<V> delegate,
        SecurityContext securityContext);

    V call() throws Exception;
}

Usage Example:

Callable<String> task = () -> {
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    return "Result from " + auth.getName();
};

// Wrap with current security context
Callable<String> secureTask = new DelegatingSecurityContextCallable<>(task);

// Submit to executor
Future<String> future = executorService.submit(secureTask);
String result = future.get();

Best Practices

Thread-Local Usage:

// Always clear context when done (especially in pooled threads)
try {
    SecurityContextHolder.setContext(context);
    // ... perform operations
} finally {
    SecurityContextHolder.clearContext();
}

Async Execution:

// Use delegating executors for automatic context propagation
Executor executor = new DelegatingSecurityContextExecutor(
    Executors.newCachedThreadPool());

executor.execute(() -> {
    // Context automatically available
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
});

Reactive Programming:

// Use withAuthentication or withSecurityContext
return webClient.get()
    .uri("/api/data")
    .retrieve()
    .bodyToMono(String.class)
    .contextWrite(ReactiveSecurityContextHolder.withAuthentication(auth));

Testing:

@BeforeEach
void setup() {
    SecurityContext context = SecurityContextHolder.createEmptyContext();
    Authentication auth = new TestingAuthenticationToken("user", "pass", "ROLE_USER");
    context.setAuthentication(auth);
    SecurityContextHolder.setContext(context);
}

@AfterEach
void cleanup() {
    SecurityContextHolder.clearContext();
}