{"record":{"id":"029598029a93b81d","repo":"spring-projects/spring-security","slug":"access-denied","errorCode":null,"errorMessage":"Access Denied","messagePattern":"Access Denied","errorType":"exception","errorClass":"AccessDeniedException","httpStatus":null,"severity":"error","filePath":"messaging/src/main/java/org/springframework/security/messaging/access/intercept/AuthorizationChannelInterceptor.java","lineNumber":75,"sourceCode":"\t * Creates a new instance.\n\t * @param preSendAuthorizationManager the {@link AuthorizationManager} to use. Cannot\n\t * be null.\n\t *\n\t */\n\tpublic AuthorizationChannelInterceptor(AuthorizationManager<Message<?>> preSendAuthorizationManager) {\n\t\tAssert.notNull(preSendAuthorizationManager, \"preSendAuthorizationManager cannot be null\");\n\t\tthis.preSendAuthorizationManager = preSendAuthorizationManager;\n\t}\n\n\t@Override\n\tpublic Message<?> preSend(Message<?> message, MessageChannel channel) {\n\t\tthis.logger.debug(LogMessage.of(() -> \"Authorizing message send\"));\n\t\tAuthorizationResult result = this.preSendAuthorizationManager.authorize(this.authentication, message);\n\t\tthis.eventPublisher.publishAuthorizationEvent(this.authentication, message, result);\n\t\tif (result == null || !result.isGranted()) { // default deny\n\t\t\tthis.logger.debug(LogMessage.of(() -> \"Failed to authorize message with authorization manager \"\n\t\t\t\t\t+ this.preSendAuthorizationManager + \" and result \" + result));\n\t\t\tthrow new AccessDeniedException(\"Access Denied\");\n\t\t}\n\t\tthis.logger.debug(LogMessage.of(() -> \"Authorized message send\"));\n\t\treturn message;\n\t}\n\n\t/**\n\t * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use\n\t * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.\n\t */\n\tpublic void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {\n\t\tthis.authentication = getAuthentication(securityContextHolderStrategy);\n\t}\n\n\t/**\n\t * Use this {@link AuthorizationEventPublisher} to publish the\n\t * {@link AuthorizationManager} result.\n\t * @param eventPublisher\n\t */","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/messaging/src/main/java/org/springframework/security/messaging/access/intercept/AuthorizationChannelInterceptor.java#L57-L93","documentation":"AuthorizationChannelInterceptor.preSend() authorizes inbound STOMP message sends using its preSendAuthorizationManager. If the AuthorizationResult is null or not granted (default-deny), it throws AccessDeniedException('Access Denied'), rejecting the message before it reaches the message handler.","triggerScenarios":"A WebSocket/STOMP client sends a message to a destination whose @PreAuthorize/@SendToUser/AuthorizationManager rules do not grant access to the current authentication, or result is null (no decision), producing a default deny in preSend().","commonSituations":"Missing or anonymous authentication on the WebSocket session (no user attached during CONNECT); security rules configured for HTTP endpoints but not for message destinations; misconfigured authorize rules or wrong destination pattern in antMatchers/MessageMatcherReactor AuthorizationManager.","solutions":["Check the authorization rules for the destination — ensure a matcher grants the user's role/authority for the message pattern.","Ensure the STOMP CONNECT carries authentication (e.g. CSRF token and session cookie, or a token header) so the user is authenticated before sends.","Inspect published authorization events/logs ('Failed to authorize message...') to see which matcher denied the send.","If anonymous access is intended, configure the AuthorizationManager to grant on the target destination."],"exampleFix":"// before: no rule for the destination\nclassifier.authorize(authentication, message) -> deny\n// after: add an explicit rule\n.authorizeMessages(\"/user/queue/**\", hasRole(\"USER\"))\n.anyMessage().denyAll();","handlingStrategy":"try-catch","validationCode":"// pre-check in a channel interceptor\nAuthentication auth = SecurityContextHolder.getContext().getAuthentication();\nif (auth == null || !auth.getAuthorities().stream()\n        .anyMatch(a -> a.getAuthority().equals(\"ROLE_USER\"))) {\n    throw new MessagingException(\"Not authorized for this destination\");\n}","typeGuard":"boolean isAuthorized(Message<?> message, Authentication auth) {\n    return auth != null && auth.isAuthenticated()\n        && auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals(\"ROLE_USER\"));\n}","tryCatchPattern":"try {\n    channel.send(message);\n} catch (AccessDeniedException | MessageDeliveryException e) {\n    // Spring wraps AccessDeniedException in MessageDeliveryException on send\n    log.warn(\"Message denied: {}\", e.getMessage());\n}","preventionTips":["Keep destination authorization rules in sync with client destination patterns.","Ensure STOMP CONNECT authenticates the user before message sends.","Enable authorization event logging to audit denials.","Test destination rules with each role in integration tests."],"tags":["websocket","stomp","access-denied","spring-security","authorization"],"backgroundTag":"permission-denied","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}