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

observation-metrics.mddocs/

Micrometer Observation and Metrics Support

Overview

Spring Security 6.0+ provides comprehensive support for Micrometer Observation API, enabling metrics collection, tracing, and monitoring of authentication and authorization operations. This integration allows applications to track security-related metrics and distributed traces.

Key Information for Agents

Core Capabilities:

  • Observation support via ObservationAuthenticationManager and ObservationAuthorizationManager
  • Wraps authentication/authorization managers to add observation/metrics
  • Context objects: AuthenticationObservationContext, AuthorizationObservationContext
  • Custom conventions: AuthenticationObservationConvention, AuthorizationObservationConvention
  • Security context change observation via ObservationSecurityContextChangedListener
  • Reactive support: ObservationReactiveAuthenticationManager, ObservationReactiveAuthorizationManager

Key Interfaces and Classes:

  • ObservationAuthenticationManager - Wraps AuthenticationManager with observation
  • ObservationAuthorizationManager<T> - Wraps AuthorizationManager with observation
  • AuthenticationObservationContext - Context: getAuthenticationRequest(), getAuthenticationResult(), getError()
  • AuthorizationObservationContext<T> - Context: getAuthentication(), getObject(), getAuthorizationResult()
  • AuthenticationObservationConvention - Interface for customizing observation naming/tags
  • AuthorizationObservationConvention<T> - Interface for authorization observation naming

Default Behaviors:

  • Observation name: "spring.security.authentications" (authentication), "spring.security.authorizations" (authorization)
  • Low cardinality tags: Authentication type, outcome (success/failure), error type
  • High cardinality tags: Username, detailed error messages (not for metrics)
  • Observations recorded for all authentication/authorization attempts
  • Context change observations: Recorded when SecurityContext changes (if listener configured)

Threading Model:

  • Observations recorded in calling thread
  • Context propagated through reactive chains automatically

Lifecycle:

  • Observation managers wrap delegate managers (decorator pattern)
  • Observation registry configured once (shared across managers)
  • Conventions set via setObservationConvention() (optional, uses default if not set)

Exceptions:

  • Observation errors: Should not affect authentication/authorization (handle gracefully)

Edge Cases:

  • Observation overhead: Minimal (~0.1-0.5ms for authentication)
  • Custom conventions: Override default naming and tag generation
  • Context propagation: Observations linked via trace context (distributed tracing)
  • Metrics vs traces: Low cardinality for metrics, high cardinality for traces only
  • Disable observations: Don't wrap managers if not needed (conditional configuration)

Core Components

Authentication Observation

ObservationAuthenticationManager

Authentication manager wrapper that records observations for authentication operations.

public class ObservationAuthenticationManager implements AuthenticationManager { .api }

Description: Decorates an AuthenticationManager to publish Micrometer observations for authentication attempts.

Constructor:

public ObservationAuthenticationManager(
    ObservationRegistry observationRegistry,
    AuthenticationManager delegate) { .api }

Parameters:

  • observationRegistry - Micrometer ObservationRegistry for recording observations
  • delegate - The underlying AuthenticationManager to decorate

Methods:

public Authentication authenticate(Authentication authentication)
    throws AuthenticationException { .api }
  • Authenticates and records observation
  • Records timing, success/failure, and context information

Package: org.springframework.security.authentication

Example:

@Configuration
public class SecurityConfig {

    @Bean
    public AuthenticationManager authenticationManager(
            ObservationRegistry observationRegistry,
            AuthenticationProvider authenticationProvider) {

        ProviderManager providerManager =
            new ProviderManager(authenticationProvider);

        // Wrap with observation support
        return new ObservationAuthenticationManager(
            observationRegistry,
            providerManager
        );
    }
}

ObservationReactiveAuthenticationManager

Reactive authentication manager with observation support.

public class ObservationReactiveAuthenticationManager
    implements ReactiveAuthenticationManager { .api }

Description: Decorates a ReactiveAuthenticationManager to publish observations.

Constructor:

public ObservationReactiveAuthenticationManager(
    ObservationRegistry observationRegistry,
    ReactiveAuthenticationManager delegate) { .api }

Methods:

public Mono<Authentication> authenticate(Authentication authentication) { .api }
  • Authenticates reactively with observation recording

Package: org.springframework.security.authentication

Example:

@Configuration
public class ReactiveSecurityConfig {

    @Bean
    public ReactiveAuthenticationManager reactiveAuthenticationManager(
            ObservationRegistry observationRegistry,
            ReactiveUserDetailsService userDetailsService) {

        UserDetailsRepositoryReactiveAuthenticationManager manager =
            new UserDetailsRepositoryReactiveAuthenticationManager(
                userDetailsService
            );

        return new ObservationReactiveAuthenticationManager(
            observationRegistry,
            manager
        );
    }
}

AuthenticationObservationContext

Context object containing authentication observation data.

public class AuthenticationObservationContext extends Observation.Context { .api }

Description: Carries authentication request, result, and error information for observations.

Methods:

public Authentication getAuthenticationRequest() { .api }
  • Returns the authentication request being processed
  • Returns: Original authentication request
public Authentication getAuthenticationResult() { .api }
  • Returns successful authentication result
  • Returns: Authenticated Authentication or null if failed
public Throwable getError() { .api }
  • Returns authentication error if failed
  • Returns: AuthenticationException or null if successful
public void setAuthenticationRequest(Authentication authentication) { .api }
  • Sets the authentication request
public void setAuthenticationResult(Authentication authentication) { .api }
  • Sets the successful authentication result
public void setError(Throwable error) { .api }
  • Sets the authentication error

Package: org.springframework.security.authentication

AuthenticationObservationConvention

Interface for customizing authentication observation naming.

public interface AuthenticationObservationConvention
    extends ObservationConvention<AuthenticationObservationContext> { .api }

Description: Naming convention for authentication observations.

Methods:

String getName() { .api }
  • Returns observation name
  • Default: "spring.security.authentications"
String getContextualName(AuthenticationObservationContext context) { .api }
  • Returns contextual name based on authentication type
KeyValues getLowCardinalityKeyValues(
    AuthenticationObservationContext context) { .api }
  • Returns low cardinality key-values (suitable for metrics dimensions)
  • Typically includes: authentication type, success/failure
KeyValues getHighCardinalityKeyValues(
    AuthenticationObservationContext context) { .api }
  • Returns high cardinality key-values (not suitable for metrics)
  • May include: username, detailed error messages

Package: org.springframework.security.authentication

Example:

public class CustomAuthenticationObservationConvention
        implements AuthenticationObservationConvention {

    @Override
    public String getName() {
        return "custom.authentication";
    }

    @Override
    public String getContextualName(AuthenticationObservationContext context) {
        Authentication auth = context.getAuthenticationRequest();
        if (auth != null) {
            return auth.getClass().getSimpleName();
        }
        return "unknown";
    }

    @Override
    public KeyValues getLowCardinalityKeyValues(
            AuthenticationObservationContext context) {
        KeyValues keyValues = KeyValues.empty();

        // Add authentication type
        Authentication auth = context.getAuthenticationRequest();
        if (auth != null) {
            keyValues = keyValues.and("auth.type",
                auth.getClass().getSimpleName());
        }

        // Add outcome
        if (context.getError() != null) {
            keyValues = keyValues.and("outcome", "failure");
            keyValues = keyValues.and("error.type",
                context.getError().getClass().getSimpleName());
        } else {
            keyValues = keyValues.and("outcome", "success");
        }

        return keyValues;
    }

    @Override
    public KeyValues getHighCardinalityKeyValues(
            AuthenticationObservationContext context) {
        // Add high cardinality data (not for metrics)
        KeyValues keyValues = KeyValues.empty();
        Authentication auth = context.getAuthenticationRequest();
        if (auth != null && auth.getName() != null) {
            keyValues = keyValues.and("username", auth.getName());
        }
        return keyValues;
    }
}

// Configure custom convention
@Bean
public ObservationRegistry observationRegistry() {
    ObservationRegistry registry = ObservationRegistry.create();
    registry.observationConfig()
        .observationConvention(new CustomAuthenticationObservationConvention());
    return registry;
}

Authorization Observation

ObservationAuthorizationManager

Authorization manager wrapper that records observations.

public class ObservationAuthorizationManager<T> implements AuthorizationManager<T> { .api }

Description: Decorates an AuthorizationManager to publish observations for authorization checks.

Type Parameters:

  • T - The type of object being secured

Constructor:

public ObservationAuthorizationManager(
    ObservationRegistry observationRegistry,
    AuthorizationManager<T> delegate) { .api }

Methods:

public AuthorizationResult authorize(
    Supplier<Authentication> authentication, T object) { .api }
  • Performs authorization check and records observation

Package: org.springframework.security.authorization

Example:

@Bean
public AuthorizationManager<RequestAuthorizationContext> authorizationManager(
        ObservationRegistry observationRegistry) {

    AuthorityAuthorizationManager<RequestAuthorizationContext> manager =
        AuthorityAuthorizationManager.hasRole("USER");

    return new ObservationAuthorizationManager<>(
        observationRegistry,
        manager
    );
}

ObservationReactiveAuthorizationManager

Reactive authorization manager with observation support.

public class ObservationReactiveAuthorizationManager<T>
    implements ReactiveAuthorizationManager<T> { .api }

Description: Decorates a ReactiveAuthorizationManager to publish observations.

Constructor:

public ObservationReactiveAuthorizationManager(
    ObservationRegistry observationRegistry,
    ReactiveAuthorizationManager<T> delegate) { .api }

Methods:

public Mono<AuthorizationDecision> check(
    Mono<Authentication> authentication, T object) { .api }
  • Performs reactive authorization check with observation

Package: org.springframework.security.authorization

AuthorizationObservationContext

Context object for authorization observations.

public class AuthorizationObservationContext<T> extends Observation.Context { .api }

Description: Carries authorization check data for observations.

Type Parameters:

  • T - Type of secured object

Methods:

public Authentication getAuthentication() { .api }
  • Returns the authentication being checked
public T getObject() { .api }
  • Returns the secured object
public AuthorizationDecision getDecision() { .api }
  • Returns the authorization decision
public void setAuthentication(Authentication authentication) { .api }
  • Sets the authentication
public void setObject(T object) { .api }
  • Sets the secured object
public void setDecision(AuthorizationDecision decision) { .api }
  • Sets the authorization decision

Package: org.springframework.security.authorization

AuthorizationObservationConvention

Interface for customizing authorization observation naming.

public interface AuthorizationObservationConvention<T>
    extends ObservationConvention<AuthorizationObservationContext<T>> { .api }

Description: Naming convention for authorization observations.

Methods:

String getName() { .api }
  • Returns observation name
  • Default: "spring.security.authorizations"
KeyValues getLowCardinalityKeyValues(
    AuthorizationObservationContext<T> context) { .api }
  • Returns low cardinality tags for metrics
  • Typically includes: object type, granted/denied

Package: org.springframework.security.authorization

Security Context Change Observation

ObservationSecurityContextChangedListener

Listener that publishes observations for SecurityContext changes.

public class ObservationSecurityContextChangedListener
    implements SecurityContextChangedListener { .api }

Description: Records observations when SecurityContext is modified.

Constructor:

public ObservationSecurityContextChangedListener(
    ObservationRegistry observationRegistry) { .api }

Methods:

public void securityContextChanged(SecurityContextChangedEvent event) { .api }
  • Handles SecurityContext change events and publishes observations

Package: org.springframework.security.core.context

Example:

@Bean
public SecurityContextChangedListener securityContextChangedListener(
        ObservationRegistry observationRegistry) {
    return new ObservationSecurityContextChangedListener(observationRegistry);
}

@Bean
public SecurityContextHolderStrategy securityContextHolderStrategy(
        SecurityContextChangedListener listener) {
    ListeningSecurityContextHolderStrategy strategy =
        new ListeningSecurityContextHolderStrategy();
    strategy.addSecurityContextChangedListener(listener);
    return strategy;
}

Configuration

Basic Observation Configuration

Enable observation support by configuring ObservationRegistry:

@Configuration
public class ObservationConfig {

    @Bean
    public ObservationRegistry observationRegistry() {
        ObservationRegistry registry = ObservationRegistry.create();

        // Add meters for metrics
        registry.observationConfig()
            .observationHandler(
                new ObservationTextPublisher(System.out::println)
            );

        return registry;
    }
}

Metrics Configuration with Micrometer

Configure full metrics collection:

@Configuration
public class MetricsConfig {

    @Bean
    public ObservationRegistry observationRegistry(
            MeterRegistry meterRegistry) {
        ObservationRegistry observationRegistry = ObservationRegistry.create();

        // Add meters handler for metrics
        observationRegistry.observationConfig()
            .observationHandler(
                new DefaultMeterObservationHandler(meterRegistry)
            );

        return observationRegistry;
    }

    @Bean
    public MeterRegistry meterRegistry() {
        // Use appropriate registry (Prometheus, etc.)
        return new SimpleMeterRegistry();
    }
}

Tracing Configuration

Configure distributed tracing with Micrometer Tracing:

@Configuration
public class TracingConfig {

    @Bean
    public ObservationRegistry observationRegistry(
            Tracer tracer,
            Propagator propagator) {
        ObservationRegistry registry = ObservationRegistry.create();

        // Add tracing handler
        registry.observationConfig()
            .observationHandler(
                new DefaultTracingObservationHandler(
                    tracer,
                    propagator,
                    new CurrentTraceContext() {
                        @Override
                        public TraceContext get() {
                            return tracer.currentSpan().context();
                        }

                        @Override
                        public Scope attach(TraceContext context) {
                            return tracer.withSpan(
                                tracer.toSpan(context)
                            );
                        }
                    }
                )
            );

        return registry;
    }
}

Complete Observability Stack

Configure metrics, tracing, and logging together:

@Configuration
public class ObservabilityConfig {

    @Bean
    public ObservationRegistry observationRegistry(
            MeterRegistry meterRegistry,
            Tracer tracer,
            Propagator propagator) {

        ObservationRegistry registry = ObservationRegistry.create();

        // Configure handlers
        ObservationConfig config = registry.observationConfig();

        // Metrics
        config.observationHandler(
            new DefaultMeterObservationHandler(meterRegistry)
        );

        // Tracing
        config.observationHandler(
            new TracingAwareMeterObservationHandler<>(
                new DefaultTracingObservationHandler(tracer, propagator),
                meterRegistry
            )
        );

        // Logging
        config.observationHandler(
            new ObservationTextPublisher(
                log -> logger.info("Observation: {}", log)
            )
        );

        return registry;
    }

    @Bean
    public AuthenticationManager authenticationManager(
            ObservationRegistry observationRegistry,
            AuthenticationProvider provider) {
        ProviderManager manager = new ProviderManager(provider);
        return new ObservationAuthenticationManager(
            observationRegistry,
            manager
        );
    }
}

Metrics Available

Authentication Metrics

Metric Name: spring.security.authentications

Tags:

  • authentication.type - Type of authentication (e.g., UsernamePasswordAuthenticationToken)
  • outcome - Success or failure
  • error.type - Type of authentication exception (if failed)

Example Queries:

# Total authentication attempts
sum(spring_security_authentications_seconds_count)

# Failed authentication rate
rate(spring_security_authentications_seconds_count{outcome="failure"}[5m])

# Average authentication duration
avg(spring_security_authentications_seconds_sum / spring_security_authentications_seconds_count)

# Authentications by type
sum by (authentication_type) (spring_security_authentications_seconds_count)

Authorization Metrics

Metric Name: spring.security.authorizations

Tags:

  • object.type - Type of secured object
  • authorization.decision - Granted or denied

Example Queries:

# Authorization denial rate
rate(spring_security_authorizations_seconds_count{authorization_decision="DENIED"}[5m])

# Authorizations by object type
sum by (object_type) (spring_security_authorizations_seconds_count)

Monitoring Examples

Dashboard Configuration (Prometheus + Grafana)

# Prometheus configuration
scrape_configs:
  - job_name: 'spring-security-app'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['localhost:8080']

Alert Rules

groups:
  - name: spring_security_alerts
    interval: 30s
    rules:
      # High authentication failure rate
      - alert: HighAuthenticationFailureRate
        expr: |
          rate(spring_security_authentications_seconds_count{outcome="failure"}[5m])
          / rate(spring_security_authentications_seconds_count[5m])
          > 0.2
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High authentication failure rate"
          description: "Authentication failure rate is {{ $value | humanizePercentage }}"

      # Authorization denial spike
      - alert: AuthorizationDenialSpike
        expr: |
          rate(spring_security_authorizations_seconds_count{authorization_decision="DENIED"}[5m])
          > 100
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "High authorization denial rate"

      # Slow authentication
      - alert: SlowAuthentication
        expr: |
          histogram_quantile(0.95,
            rate(spring_security_authentications_seconds_bucket[5m])
          ) > 1
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "95th percentile authentication time exceeds 1 second"

Custom Metrics

Emit custom security metrics:

@Component
public class SecurityMetrics {

    private final MeterRegistry meterRegistry;
    private final Counter loginAttempts;
    private final Counter failedLogins;

    public SecurityMetrics(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
        this.loginAttempts = Counter.builder("security.login.attempts")
            .description("Total login attempts")
            .register(meterRegistry);
        this.failedLogins = Counter.builder("security.login.failures")
            .description("Failed login attempts")
            .tag("reason", "bad_credentials")
            .register(meterRegistry);
    }

    @EventListener
    public void onAuthenticationSuccess(AuthenticationSuccessEvent event) {
        loginAttempts.increment();
    }

    @EventListener
    public void onAuthenticationFailure(
            AbstractAuthenticationFailureEvent event) {
        loginAttempts.increment();
        failedLogins.increment();
    }
}

Distributed Tracing

Trace Context Propagation

Traces propagate automatically across:

  • HTTP requests
  • Async method calls (with proper executor configuration)
  • Message queues (with appropriate instrumentation)

Example trace hierarchy:

HTTP Request (trace ID: abc123)
  ├─ Authentication (span)
  │   ├─ UserDetailsService.loadUserByUsername (span)
  │   └─ PasswordEncoder.matches (span)
  ├─ Authorization (span)
  │   └─ PermissionEvaluator.hasPermission (span)
  └─ Business Logic (span)

Custom Spans

Add custom spans for detailed tracing:

@Service
public class CustomAuthenticationService {

    private final Tracer tracer;
    private final ObservationRegistry observationRegistry;

    public Authentication customAuthenticate(String username, String password) {
        // Create custom observation
        return Observation.createNotStarted(
            "custom.authentication",
            observationRegistry
        )
        .lowCardinalityKeyValue("username", username)
        .observe(() -> {
            // Authentication logic with automatic span creation
            return performAuthentication(username, password);
        });
    }
}

Best Practices

  1. Enable observations in production - Essential for monitoring security
  2. Use low cardinality tags - Prevent metric explosion
  3. Configure appropriate retention - Security metrics need historical analysis
  4. Set up alerts - Monitor authentication failures and authorization denials
  5. Correlate with application metrics - Link security events to business metrics
  6. Use distributed tracing - Track security checks across services
  7. Monitor performance - Track authentication/authorization duration
  8. Secure metrics endpoints - Protect actuator endpoints

Performance Considerations

Observations have minimal overhead:

  • Authentication: ~0.1-0.5ms overhead
  • Authorization: ~0.05-0.2ms overhead
  • Memory: ~100 bytes per observation context

Disable in performance-critical scenarios:

// Conditional observation
@Bean
@ConditionalOnProperty(
    name = "spring.security.observation.enabled",
    havingValue = "true",
    matchIfMissing = true
)
public ObservationAuthenticationManager observableAuthenticationManager(...) {
    // Observation-enabled manager
}

See Also

  • Authentication Architecture
  • Authorization
  • Authentication Events
  • Micrometer documentation
  • Spring Boot Actuator documentation