CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-spring-security-messaging

Spring Security support for messaging and WebSocket applications

Pending
Overview
Eval results
Files

Spring Security Messaging

Spring Security Messaging provides comprehensive security support for Spring's messaging abstractions and WebSocket applications. It enables developers to integrate authentication and authorization into message-based and real-time communication systems with channel interceptors, authorization managers, and reactive support.

Package Information

  • Package Name: spring-security-messaging
  • Group ID: org.springframework.security
  • Artifact ID: spring-security-messaging
  • Package Type: maven
  • Language: Java
  • Version: 7.0.0
  • Installation:
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-messaging</artifactId>
        <version>7.0.0</version>
    </dependency>

Dependencies

Spring Security Messaging requires:

  • Spring Framework 6.0+
  • Spring Security Core 7.0+
  • Spring Messaging (included in Spring Framework)
  • For reactive support: Project Reactor (reactor-core)

Maven Dependencies:

<dependencies>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-messaging</artifactId>
        <version>7.0.0</version>
    </dependency>
    <!-- For reactive support -->
    <dependency>
        <groupId>io.projectreactor</groupId>
        <artifactId>reactor-core</artifactId>
    </dependency>
    <!-- For WebSocket support -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-websocket</artifactId>
    </dependency>
</dependencies>

Core Imports

// Authorization
import org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager;
import org.springframework.security.messaging.access.intercept.AuthorizationChannelInterceptor;
import org.springframework.security.messaging.access.intercept.MessageAuthorizationContext;

// Security Context
import org.springframework.security.messaging.context.SecurityContextChannelInterceptor;
import org.springframework.security.messaging.context.SecurityContextPropagationChannelInterceptor;
import org.springframework.security.messaging.context.AuthenticationPrincipalArgumentResolver;

// CSRF Protection
import org.springframework.security.messaging.web.csrf.CsrfChannelInterceptor;
import org.springframework.security.messaging.web.csrf.XorCsrfChannelInterceptor;
import org.springframework.security.messaging.web.socket.server.CsrfTokenHandshakeInterceptor;

// Message Matching
import org.springframework.security.messaging.access.intercept.MessageMatcher;
import org.springframework.security.messaging.access.intercept.PathPatternMessageMatcher;
import org.springframework.security.messaging.access.intercept.SimpMessageTypeMatcher;
import org.springframework.security.messaging.access.intercept.AndMessageMatcher;
import org.springframework.security.messaging.access.intercept.OrMessageMatcher;

// Expression Handling
import org.springframework.security.messaging.access.expression.DefaultMessageSecurityExpressionHandler;
import org.springframework.security.messaging.access.expression.MessageAuthorizationContextSecurityExpressionHandler;

// Reactive Support
import org.springframework.security.messaging.handler.invocation.reactive.AuthenticationPrincipalArgumentResolver;
import org.springframework.security.messaging.handler.invocation.reactive.CurrentSecurityContextArgumentResolver;

Quick Start

Minimal WebSocket Security Configuration

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.security.config.annotation.web.socket.AbstractSecurityWebSocketMessageBrokerConfigurer;
import org.springframework.security.messaging.access.intercept.MessageMatcherDelegatingAuthorizationManager;
import org.springframework.security.messaging.access.intercept.AuthorizationChannelInterceptor;
import org.springframework.security.messaging.context.SecurityContextPropagationChannelInterceptor;

@Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {

    @Override
    protected void configureInbound(ChannelRegistration registration) {
        // Propagate security context (required for @AuthenticationPrincipal)
        registration.interceptors(new SecurityContextPropagationChannelInterceptor());

        // Configure authorization
        MessageMatcherDelegatingAuthorizationManager authManager =
            MessageMatcherDelegatingAuthorizationManager.builder()
                .nullDestMatcher().authenticated()
                .simpSubscribeDestMatchers("/user/queue/errors").permitAll()
                .simpDestMatchers("/app/**").hasRole("USER")
                .simpSubscribeDestMatchers("/topic/**").hasRole("USER")
                .anyMessage().denyAll()
                .build();

        registration.interceptors(new AuthorizationChannelInterceptor(authManager));
    }
}

Accessing Authenticated User in Message Handlers

import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;

@Controller
public class ChatController {

    @MessageMapping("/chat")
    public void processMessage(@AuthenticationPrincipal UserDetails user, String message) {
        // user contains the authenticated principal
        // Throws IllegalArgumentException if user is null and no anonymous auth configured
        String username = user.getUsername();
        // Process message...
    }

    // Handle null principal gracefully
    @MessageMapping("/chat-safe")
    public void processMessageSafe(
        @AuthenticationPrincipal(required = false) UserDetails user,
        String message
    ) {
        if (user != null) {
            String username = user.getUsername();
            // Process authenticated message...
        } else {
            // Handle anonymous/unauthenticated message...
        }
    }
}

Architecture

Spring Security Messaging is organized around several key functional areas:

  • Authorization System: Message-based access control using pattern matching and authorization managers
  • Security Context Management: Propagation of authentication across message handling threads
  • CSRF Protection: Token-based CSRF validation for WebSocket connections
  • Message Matching: Flexible pattern matching for destinations, message types, and custom criteria
  • Reactive Support: Reactive argument resolvers for Project Reactor-based messaging
  • Expression Evaluation: SpEL-based security expressions for complex authorization rules

Capabilities

Message Authorization

Configure fine-grained access control rules for messages based on destination patterns, message types, and custom criteria. The primary API for securing message channels with role-based and expression-based authorization.

/**
 * Authorization manager that delegates based on MessageMatcher patterns.
 * Thread-safe for concurrent use.
 *
 * @since 5.8
 */
public final class MessageMatcherDelegatingAuthorizationManager
    implements AuthorizationManager<Message<?>> {

    /**
     * Creates a new builder for configuring authorization rules.
     *
     * @return a Builder instance
     */
    public static Builder builder();

    /**
     * Performs authorization check on the message.
     *
     * @param authentication supplier of authentication to check (must not be null)
     * @param message the message to authorize (must not be null)
     * @return authorization result (never null)
     * @throws IllegalArgumentException if authentication or message is null
     */
    public AuthorizationResult authorize(
        Supplier<? extends Authentication> authentication,
        Message<?> message
    );
}

Builder API:

public static final class Builder {
    /**
     * Matches any message. Should typically be last in chain.
     *
     * @return Constraint for configuring authorization rule
     */
    public Constraint anyMessage();

    /**
     * Matches messages with null destination.
     *
     * @return Constraint for configuring authorization rule
     */
    public Constraint nullDestMatcher();

    /**
     * Matches messages by STOMP message type.
     *
     * @param typesToMatch the STOMP message types to match (must not be null or empty)
     * @return Constraint for configuring authorization rule
     * @throws IllegalArgumentException if typesToMatch is null or empty
     */
    public Constraint simpTypeMatchers(SimpMessageType... typesToMatch);

    /**
     * Matches messages by destination pattern for any message type.
     * Uses Ant-style path patterns.
     *
     * @param patterns destination patterns (must not be null or empty)
     * @return Constraint for configuring authorization rule
     * @throws IllegalArgumentException if patterns is null or empty
     */
    public Constraint simpDestMatchers(String... patterns);

    /**
     * Matches MESSAGE type messages by destination pattern.
     *
     * @param patterns destination patterns (must not be null or empty)
     * @return Constraint for configuring authorization rule
     */
    public Constraint simpMessageDestMatchers(String... patterns);

    /**
     * Matches SUBSCRIBE type messages by destination pattern.
     *
     * @param patterns destination patterns (must not be null or empty)
     * @return Constraint for configuring authorization rule
     */
    public Constraint simpSubscribeDestMatchers(String... patterns);

    /**
     * Matches using custom message matchers.
     *
     * @param matchers custom MessageMatcher instances (must not be null or empty)
     * @return Constraint for configuring authorization rule
     * @throws IllegalArgumentException if matchers is null or empty
     */
    public Constraint matchers(MessageMatcher<?>... matchers);

    /**
     * Builds the MessageMatcherDelegatingAuthorizationManager.
     *
     * @return configured authorization manager (never null)
     * @throws IllegalStateException if no rules are configured
     */
    public MessageMatcherDelegatingAuthorizationManager build();
}

Constraint API:

public interface Constraint {
    /**
     * Requires user to have the specified role (ROLE_ prefix added automatically).
     *
     * @param role the role name without ROLE_ prefix (must not be null or empty)
     * @return Builder for configuring additional rules
     * @throws IllegalArgumentException if role is null or empty
     */
    Builder hasRole(String role);

    /**
     * Requires user to have any of the specified roles.
     *
     * @param roles role names without ROLE_ prefix (must not be null or empty)
     * @return Builder for configuring additional rules
     */
    Builder hasAnyRole(String... roles);

    /**
     * Requires user to have the specified authority.
     *
     * @param authority the authority name (must not be null or empty)
     * @return Builder for configuring additional rules
     */
    Builder hasAuthority(String authority);

    /**
     * Requires user to have any of the specified authorities.
     *
     * @param authorities authority names (must not be null or empty)
     * @return Builder for configuring additional rules
     */
    Builder hasAnyAuthority(String... authorities);

    /**
     * Permits all users (authenticated and anonymous).
     *
     * @return Builder for configuring additional rules
     */
    Builder permitAll();

    /**
     * Denies all users.
     *
     * @return Builder for configuring additional rules
     */
    Builder denyAll();

    /**
     * Requires user to be authenticated (including remember-me).
     *
     * @return Builder for configuring additional rules
     */
    Builder authenticated();

    /**
     * Requires full authentication (not remember-me).
     *
     * @return Builder for configuring additional rules
     */
    Builder fullyAuthenticated();

    /**
     * Requires remember-me authentication.
     *
     * @return Builder for configuring additional rules
     */
    Builder rememberMe();

    /**
     * Requires anonymous authentication.
     *
     * @return Builder for configuring additional rules
     */
    Builder anonymous();

    /**
     * Uses custom AuthorizationManager for authorization decision.
     *
     * @param manager custom authorization manager (must not be null)
     * @return Builder for configuring additional rules
     * @throws IllegalArgumentException if manager is null
     */
    Builder access(AuthorizationManager<MessageAuthorizationContext<?>> manager);
}

Message Authorization

Security Context Propagation

Manage authentication context across message processing threads. Choose between classic header-based propagation or modern context-capturing approach.

/**
 * Classic approach: obtains Authentication from message headers.
 * Thread-safe when used with proper SecurityContextHolderStrategy.
 *
 * @since 4.0
 */
public final class SecurityContextChannelInterceptor
    implements ExecutorChannelInterceptor, ChannelInterceptor {

    /**
     * Default header name: "simpUser"
     */
    public static final String USER_HEADER = "simpUser";

    /**
     * Creates interceptor using default USER_HEADER.
     */
    public SecurityContextChannelInterceptor();

    /**
     * Creates interceptor with custom header name.
     *
     * @param authenticationHeaderName name of header containing authentication (must not be null)
     * @throws IllegalArgumentException if authenticationHeaderName is null
     */
    public SecurityContextChannelInterceptor(String authenticationHeaderName);

    /**
     * Sets anonymous authentication to use when no authentication is present.
     *
     * @param authentication the anonymous authentication (can be null)
     */
    public void setAnonymousAuthentication(Authentication authentication);

    /**
     * Sets the SecurityContextHolderStrategy for managing security context.
     *
     * @param strategy the strategy to use (must not be null)
     * @throws IllegalArgumentException if strategy is null
     */
    public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy strategy);

    /**
     * Intercepts message before sending and sets up security context.
     *
     * @param message the message being sent (must not be null)
     * @param channel the channel (must not be null)
     * @return the message (never null)
     * @throws IllegalArgumentException if message or channel is null
     */
    public Message<?> preSend(Message<?> message, MessageChannel channel);

    /**
     * Cleans up security context after message send completes.
     *
     * @param message the message (must not be null)
     * @param channel the channel (must not be null)
     * @param sent whether send succeeded
     * @param ex exception if send failed (can be null)
     */
    public void afterSendCompletion(
        Message<?> message,
        MessageChannel channel,
        boolean sent,
        Exception ex
    );
}
/**
 * Modern approach (since 6.2): captures Authentication from current SecurityContext.
 * Cannot be used together with SecurityContextChannelInterceptor.
 * Thread-safe when used with proper SecurityContextHolderStrategy.
 *
 * @since 6.2
 */
public final class SecurityContextPropagationChannelInterceptor
    implements ExecutorChannelInterceptor {

    /**
     * Default header name: "simpUser"
     */
    public static final String USER_HEADER = "simpUser";

    /**
     * Creates interceptor using default USER_HEADER.
     */
    public SecurityContextPropagationChannelInterceptor();

    /**
     * Creates interceptor with custom header name.
     *
     * @param authenticationHeaderName name of header to store authentication (must not be null)
     * @throws IllegalArgumentException if authenticationHeaderName is null
     */
    public SecurityContextPropagationChannelInterceptor(String authenticationHeaderName);

    /**
     * Sets the SecurityContextHolderStrategy for managing security context.
     *
     * @param strategy the strategy to use (must not be null)
     * @throws IllegalArgumentException if strategy is null
     */
    public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy strategy);

    /**
     * Sets anonymous authentication to use when no authentication is present.
     *
     * @param authentication the anonymous authentication (can be null)
     */
    public void setAnonymousAuthentication(Authentication authentication);

    /**
     * Captures current authentication and stores in message header.
     *
     * @param message the message (must not be null)
     * @param channel the channel (must not be null)
     * @return message with authentication in header (never null)
     * @throws IllegalArgumentException if message or channel is null
     */
    public Message<?> preSend(Message<?> message, MessageChannel channel);
}

Security Context

CSRF Protection

Protect WebSocket connections from Cross-Site Request Forgery attacks using token validation during handshake and message processing.

/**
 * HandshakeInterceptor that loads CsrfToken from HTTP request during
 * WebSocket handshake and stores in WebSocket session attributes.
 * Thread-safe.
 *
 * @since 4.0
 */
public final class CsrfTokenHandshakeInterceptor implements HandshakeInterceptor {

    /**
     * Loads CSRF token from request and stores in WebSocket attributes.
     *
     * @param request the HTTP request (must not be null)
     * @param response the HTTP response (must not be null)
     * @param wsHandler the WebSocket handler (must not be null)
     * @param attributes WebSocket session attributes (mutable, must not be null)
     * @return true to proceed with handshake, false to reject
     * @throws IllegalArgumentException if any parameter is null
     */
    public boolean beforeHandshake(
        ServerHttpRequest request,
        ServerHttpResponse response,
        WebSocketHandler wsHandler,
        Map<String, Object> attributes
    );

    /**
     * Called after handshake completion (no-op implementation).
     *
     * @param request the HTTP request (must not be null)
     * @param response the HTTP response (must not be null)
     * @param wsHandler the WebSocket handler (must not be null)
     * @param exception exception if handshake failed (can be null)
     */
    public void afterHandshake(
        ServerHttpRequest request,
        ServerHttpResponse response,
        WebSocketHandler wsHandler,
        Exception exception
    );
}
/**
 * ChannelInterceptor that validates CSRF token in CONNECT messages.
 * Expects token to be present in message headers (loaded by handshake interceptor).
 * Thread-safe.
 *
 * @since 4.0
 */
public final class CsrfChannelInterceptor implements ChannelInterceptor {

    /**
     * Intercepts message before sending and validates CSRF token for CONNECT messages.
     *
     * @param message the message (must not be null)
     * @param channel the channel (must not be null)
     * @return the message if valid (never null)
     * @throws InvalidCsrfTokenException if CSRF token is invalid or missing for CONNECT messages
     * @throws IllegalArgumentException if message or channel is null
     */
    public Message<?> preSend(Message<?> message, MessageChannel channel);
}
/**
 * ChannelInterceptor that validates XOR-masked CSRF tokens in CONNECT messages.
 * Use with XorCsrfTokenRequestAttributeHandler for enhanced CSRF protection.
 * Thread-safe.
 *
 * @since 5.8
 */
public final class XorCsrfChannelInterceptor implements ChannelInterceptor {

    /**
     * Intercepts message and validates XOR-masked CSRF token for CONNECT messages.
     *
     * @param message the message (must not be null)
     * @param channel the channel (must not be null)
     * @return the message if valid (never null)
     * @throws InvalidCsrfTokenException if CSRF token is invalid or missing for CONNECT messages
     * @throws IllegalArgumentException if message or channel is null
     */
    public Message<?> preSend(Message<?> message, MessageChannel channel);
}

CSRF Protection

Message Matching

Match messages based on destination patterns, message types, or custom criteria. Supports path variables and composite matching logic.

/**
 * Strategy interface for matching messages.
 * Thread-safe implementations are recommended.
 *
 * @param <T> the message payload type
 * @since 4.0
 */
public interface MessageMatcher<T> {

    /**
     * Returns true if this matcher matches the given message.
     *
     * @param message the message to match (must not be null)
     * @return true if message matches
     * @throws IllegalArgumentException if message is null
     */
    boolean matches(Message<? extends T> message);

    /**
     * Returns match result with extracted path variables.
     *
     * @param message the message to match (must not be null)
     * @return match result including extracted variables (never null)
     * @since 6.5
     */
    default MatchResult matcher(Message<? extends T> message) {
        return matches(message) ? MatchResult.match() : MatchResult.notMatch();
    }

    /**
     * Matcher that matches every message.
     */
    MessageMatcher<Object> ANY_MESSAGE = (message) -> true;
}
/**
 * MessageMatcher that matches based on destination path patterns.
 * Supports path variables like /app/user/{userId}/*.
 * Thread-safe.
 *
 * @since 6.5
 */
public final class PathPatternMessageMatcher implements MessageMatcher<Object> {

    /**
     * Matcher for messages with null destination.
     */
    public static final MessageMatcher<Object> NULL_DESTINATION_MATCHER;

    /**
     * Creates builder with default PathPatternParser.
     *
     * @return builder instance (never null)
     */
    public static Builder withDefaults();

    /**
     * Creates builder with custom PathPatternParser.
     *
     * @param parser the path pattern parser to use (must not be null)
     * @return builder instance (never null)
     * @throws IllegalArgumentException if parser is null
     */
    public static Builder withPathPatternParser(PathPatternParser parser);

    /**
     * Returns true if message destination matches the pattern.
     *
     * @param message the message to match (must not be null)
     * @return true if destination matches pattern
     * @throws IllegalArgumentException if message is null
     */
    public boolean matches(Message<?> message);

    /**
     * Returns match result with extracted path variables.
     *
     * @param message the message to match (must not be null)
     * @return match result with variables (never null)
     * @throws IllegalArgumentException if message is null
     */
    public MatchResult matcher(Message<?> message);
}
/**
 * MessageMatcher that matches based on STOMP message type.
 * Thread-safe.
 *
 * @since 4.0
 */
public class SimpMessageTypeMatcher implements MessageMatcher<Object> {

    /**
     * Creates matcher for the specified message type.
     *
     * @param typeToMatch the STOMP message type to match (must not be null)
     * @throws IllegalArgumentException if typeToMatch is null
     */
    public SimpMessageTypeMatcher(SimpMessageType typeToMatch);

    /**
     * Returns true if message has the matching type.
     *
     * @param message the message to match (must not be null)
     * @return true if message type matches
     * @throws IllegalArgumentException if message is null
     */
    public boolean matches(Message<?> message);
}

Message Matching

Authentication Principal Resolution

Inject authenticated user principals directly into message handler method parameters using the @AuthenticationPrincipal annotation.

/**
 * HandlerMethodArgumentResolver that resolves parameters annotated
 * with @AuthenticationPrincipal to inject the authenticated principal.
 * Thread-safe.
 *
 * @since 4.0
 */
public final class AuthenticationPrincipalArgumentResolver
    implements HandlerMethodArgumentResolver {

    /**
     * Checks if this resolver supports the given parameter.
     *
     * @param parameter the method parameter (must not be null)
     * @return true if parameter is annotated with @AuthenticationPrincipal
     * @throws IllegalArgumentException if parameter is null
     */
    public boolean supportsParameter(MethodParameter parameter);

    /**
     * Resolves the authenticated principal from SecurityContext.
     *
     * @param parameter the method parameter (must not be null)
     * @param message the current message (must not be null)
     * @return the authenticated principal (can be null if required=false)
     * @throws IllegalArgumentException if principal is required but not found
     * @throws IllegalArgumentException if parameter or message is null
     */
    public Object resolveArgument(MethodParameter parameter, Message<?> message);

    /**
     * Sets the SecurityContextHolderStrategy for obtaining authentication.
     *
     * @param securityContextHolderStrategy the strategy to use (must not be null)
     * @throws IllegalArgumentException if strategy is null
     * @since 5.8
     */
    public void setSecurityContextHolderStrategy(
        SecurityContextHolderStrategy securityContextHolderStrategy
    );

    /**
     * Sets template defaults for annotation expression evaluation.
     *
     * @param templateDefaults the template defaults (can be null)
     * @since 6.4
     */
    public void setTemplateDefaults(
        AnnotationTemplateExpressionDefaults templateDefaults
    );
}

Security Context

Reactive Support

Support for reactive messaging applications using Project Reactor, providing reactive argument resolvers for @AuthenticationPrincipal and @CurrentSecurityContext.

/**
 * Reactive HandlerMethodArgumentResolver for @AuthenticationPrincipal parameters.
 * Returns Mono<Object> to support reactive message handling.
 * Thread-safe.
 *
 * @since 5.2
 */
// In package: org.springframework.security.messaging.handler.invocation.reactive
public class AuthenticationPrincipalArgumentResolver
    implements HandlerMethodArgumentResolver {

    /**
     * Sets the BeanResolver for resolving beans in SpEL expressions.
     *
     * @param beanResolver the bean resolver (can be null)
     */
    public void setBeanResolver(BeanResolver beanResolver);

    /**
     * Sets the ReactiveAdapterRegistry for reactive type conversions.
     *
     * @param adapterRegistry the adapter registry (must not be null)
     * @throws IllegalArgumentException if adapterRegistry is null
     */
    public void setAdapterRegistry(ReactiveAdapterRegistry adapterRegistry);

    /**
     * Checks if this resolver supports the given parameter.
     *
     * @param parameter the method parameter (must not be null)
     * @return true if parameter is annotated with @AuthenticationPrincipal
     * @throws IllegalArgumentException if parameter is null
     */
    public boolean supportsParameter(MethodParameter parameter);

    /**
     * Resolves the authenticated principal reactively from ReactiveSecurityContextHolder.
     *
     * @param parameter the method parameter (must not be null)
     * @param message the current message (must not be null)
     * @return Mono containing the authenticated principal (never null, may emit null)
     * @throws IllegalArgumentException if parameter or message is null
     */
    public Mono<Object> resolveArgument(MethodParameter parameter, Message<?> message);

    /**
     * Sets template defaults for annotation expression evaluation.
     *
     * @param templateDefaults the template defaults (can be null)
     * @since 6.4
     */
    public void setTemplateDefaults(
        AnnotationTemplateExpressionDefaults templateDefaults
    );
}
/**
 * Reactive HandlerMethodArgumentResolver for @CurrentSecurityContext parameters.
 * Returns Mono<SecurityContext> to support reactive message handling.
 * Thread-safe.
 *
 * @since 5.2
 */
// In package: org.springframework.security.messaging.handler.invocation.reactive
public class CurrentSecurityContextArgumentResolver
    implements HandlerMethodArgumentResolver {

    /**
     * Sets the BeanResolver for resolving beans in SpEL expressions.
     *
     * @param beanResolver the bean resolver (can be null)
     */
    public void setBeanResolver(BeanResolver beanResolver);

    /**
     * Sets the ReactiveAdapterRegistry for reactive type conversions.
     *
     * @param adapterRegistry the adapter registry (must not be null)
     * @throws IllegalArgumentException if adapterRegistry is null
     */
    public void setAdapterRegistry(ReactiveAdapterRegistry adapterRegistry);

    /**
     * Checks if this resolver supports the given parameter.
     *
     * @param parameter the method parameter (must not be null)
     * @return true if parameter is supported
     * @throws IllegalArgumentException if parameter is null
     */
    public boolean supportsParameter(MethodParameter parameter);

    /**
     * Resolves the SecurityContext reactively from ReactiveSecurityContextHolder.
     *
     * @param parameter the method parameter (must not be null)
     * @param message the current message (must not be null)
     * @return Mono containing the SecurityContext (never null, may emit null)
     * @throws IllegalArgumentException if parameter or message is null
     */
    public Mono<Object> resolveArgument(MethodParameter parameter, Message<?> message);

    /**
     * Sets template defaults for annotation expression evaluation.
     *
     * @param templateDefaults the template defaults (can be null)
     * @since 6.4
     */
    public void setTemplateDefaults(
        AnnotationTemplateExpressionDefaults templateDefaults
    );
}

Reactive Support

Expression-Based Authorization

Use Spring Expression Language (SpEL) for complex authorization rules with custom security expressions and evaluation contexts.

/**
 * Default SecurityExpressionHandler for Message-based security expressions.
 * Creates evaluation contexts with MessageSecurityExpressionRoot.
 * Thread-safe.
 *
 * @param <T> the message payload type
 * @since 4.0
 */
public class DefaultMessageSecurityExpressionHandler<T>
    extends AbstractSecurityExpressionHandler<Message<T>> {

    /**
     * Creates evaluation context for the given authentication and message.
     *
     * @param authentication supplier of authentication to evaluate (must not be null)
     * @param message the message being evaluated (must not be null)
     * @return evaluation context for SpEL expressions (never null)
     * @throws IllegalArgumentException if authentication or message is null
     */
    public EvaluationContext createEvaluationContext(
        Supplier<? extends Authentication> authentication,
        Message<T> message
    );
}
/**
 * SecurityExpressionHandler for MessageAuthorizationContext that supports
 * path variables extracted from destination patterns.
 * Thread-safe.
 *
 * @since 5.8
 */
public final class MessageAuthorizationContextSecurityExpressionHandler
    implements SecurityExpressionHandler<MessageAuthorizationContext<?>> {

    /**
     * Creates handler with default DefaultMessageSecurityExpressionHandler.
     */
    public MessageAuthorizationContextSecurityExpressionHandler();

    /**
     * Creates handler wrapping the specified delegate.
     *
     * @param expressionHandler delegate handler for Message expressions (must not be null)
     * @throws IllegalArgumentException if expressionHandler is null
     */
    public MessageAuthorizationContextSecurityExpressionHandler(
        SecurityExpressionHandler<Message<?>> expressionHandler
    );

    /**
     * Returns the expression parser used for parsing SpEL expressions.
     *
     * @return the expression parser (never null)
     */
    public ExpressionParser getExpressionParser();

    /**
     * Creates evaluation context for the given authentication and context.
     *
     * @param authentication the authentication to evaluate (must not be null)
     * @param context the message authorization context (must not be null)
     * @return evaluation context for SpEL expressions (never null)
     * @throws IllegalArgumentException if authentication or context is null
     */
    public EvaluationContext createEvaluationContext(
        Authentication authentication,
        MessageAuthorizationContext<?> context
    );

    /**
     * Creates evaluation context with authentication supplier.
     *
     * @param authentication supplier of authentication to evaluate (must not be null)
     * @param context the message authorization context (must not be null)
     * @return evaluation context for SpEL expressions (never null)
     * @throws IllegalArgumentException if authentication or context is null
     */
    public EvaluationContext createEvaluationContext(
        Supplier<? extends Authentication> authentication,
        MessageAuthorizationContext<?> context
    );
}

Expression Handling

Types

MessageAuthorizationContext

Context object holding a message and extracted path variables for authorization decisions.

/**
 * Holds a Message and extracted path variables for authorization.
 * Immutable and thread-safe.
 *
 * @param <T> the message payload type
 * @since 5.8
 */
public final class MessageAuthorizationContext<T> {

    /**
     * Creates context with message only.
     *
     * @param message the message (must not be null)
     * @throws IllegalArgumentException if message is null
     */
    public MessageAuthorizationContext(Message<T> message);

    /**
     * Creates context with message and path variables.
     *
     * @param message the message (must not be null)
     * @param variables path variables extracted from destination (can be null, treated as empty)
     * @throws IllegalArgumentException if message is null
     */
    public MessageAuthorizationContext(Message<T> message, Map<String, String> variables);

    /**
     * Returns the message.
     *
     * @return the message (never null)
     */
    public Message<T> getMessage();

    /**
     * Returns extracted path variables from destination pattern matching.
     *
     * @return map of variable names to values (never null, may be empty)
     */
    public Map<String, String> getVariables();
}

AuthorizationChannelInterceptor

Channel interceptor that performs authorization checks on messages before they are sent.

/**
 * ChannelInterceptor that performs authorization on messages before they are sent.
 * Thread-safe.
 *
 * @since 5.8
 */
public final class AuthorizationChannelInterceptor implements ChannelInterceptor {

    /**
     * Creates interceptor with the specified authorization manager.
     *
     * @param preSendAuthorizationManager authorization manager for pre-send checks (must not be null)
     * @throws IllegalArgumentException if preSendAuthorizationManager is null
     */
    public AuthorizationChannelInterceptor(
        AuthorizationManager<Message<?>> preSendAuthorizationManager
    );

    /**
     * Intercepts message before sending and performs authorization check.
     *
     * @param message the message to authorize (must not be null)
     * @param channel the channel (must not be null)
     * @return the message if authorized (never null)
     * @throws AccessDeniedException if authorization fails
     * @throws IllegalArgumentException if message or channel is null
     */
    public Message<?> preSend(Message<?> message, MessageChannel channel);

    /**
     * Sets the SecurityContextHolderStrategy for obtaining authentication.
     *
     * @param securityContextHolderStrategy the strategy to use (must not be null)
     * @throws IllegalArgumentException if strategy is null
     */
    public void setSecurityContextHolderStrategy(
        SecurityContextHolderStrategy securityContextHolderStrategy
    );

    /**
     * Sets the publisher for authorization events.
     *
     * @param eventPublisher the event publisher (can be null)
     */
    public void setAuthorizationEventPublisher(
        AuthorizationEventPublisher eventPublisher
    );
}

MatchResult

Result of a message matching operation, including match status and extracted path variables.

/**
 * Result of a message matching operation.
 * Immutable and thread-safe.
 *
 * @since 6.5
 */
public interface MatchResult {

    /**
     * Returns whether the message matched.
     *
     * @return true if matched
     */
    boolean isMatch();

    /**
     * Returns extracted path variables from pattern matching.
     *
     * @return map of variable names to values (never null, may be empty)
     */
    Map<String, String> getVariables();

    /**
     * Creates a match result with no variables.
     *
     * @return match result (never null)
     */
    static MatchResult match();

    /**
     * Creates a match result with extracted variables.
     *
     * @param variables the extracted path variables (can be null, treated as empty)
     * @return match result (never null)
     */
    static MatchResult match(Map<String, String> variables);

    /**
     * Creates a non-match result.
     *
     * @return non-match result (never null)
     */
    static MatchResult notMatch();
}

Error Handling

Common Exceptions

  • AccessDeniedException: Thrown when authorization fails
  • InvalidCsrfTokenException: Thrown when CSRF token validation fails
  • IllegalArgumentException: Thrown when null parameters are provided
  • IllegalStateException: Thrown when configuration is invalid

Error Handling Example

@Controller
public class SecureMessageController {

    @MessageMapping("/secure")
    public void handleSecureMessage(
        @AuthenticationPrincipal UserDetails user,
        String message
    ) {
        try {
            // Process message...
        } catch (AccessDeniedException e) {
            // Handle authorization failure
            logger.error("Access denied for user: {}", user.getUsername(), e);
        } catch (IllegalArgumentException e) {
            // Handle invalid input
            logger.error("Invalid message format", e);
        }
    }
}

Thread Safety

All components in Spring Security Messaging are designed to be thread-safe:

  • MessageMatcherDelegatingAuthorizationManager: Thread-safe for concurrent authorization checks
  • SecurityContextChannelInterceptor: Thread-safe when used with proper SecurityContextHolderStrategy
  • SecurityContextPropagationChannelInterceptor: Thread-safe when used with proper SecurityContextHolderStrategy
  • CsrfChannelInterceptor: Thread-safe
  • AuthorizationChannelInterceptor: Thread-safe
  • MessageMatcher implementations: Should be thread-safe

Important: When using custom SecurityContextHolderStrategy, ensure it's thread-safe for your use case.

Performance Considerations

  • Authorization checks: Typically < 1ms overhead per message
  • Security context propagation: Minimal overhead (~0.1ms)
  • CSRF validation: ~0.2ms overhead for CONNECT messages only
  • Expression evaluation: Varies with expression complexity (typically 0.5-2ms)

For high-throughput scenarios, consider:

  • Caching authorization decisions when appropriate
  • Using simple role checks instead of complex expressions
  • Minimizing path variable extraction for frequently matched patterns

Troubleshooting

Issue: @AuthenticationPrincipal returns null

Cause: Security context not propagated to message handling thread.

Solution: Ensure SecurityContextPropagationChannelInterceptor or SecurityContextChannelInterceptor is registered:

@Override
protected void configureInbound(ChannelRegistration registration) {
    registration.interceptors(new SecurityContextPropagationChannelInterceptor());
}

Issue: Authorization always denies

Cause: No matching rule or rules evaluated in wrong order.

Solution: Ensure rules are ordered from most specific to least specific, and include .anyMessage().denyAll() as final rule if needed.

Issue: CSRF token validation fails

Cause: Token not included in CONNECT frame headers or token handler mismatch.

Solution: Ensure CsrfTokenHandshakeInterceptor is registered and client includes token in CONNECT headers. Match interceptor type (CsrfChannelInterceptor vs XorCsrfChannelInterceptor) with token handler.

Install with Tessl CLI

npx tessl i tessl/maven-spring-security-messaging
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
mavenpkg:maven/org.springframework.security/spring-security-messaging@7.0.x