I have the following demo app this includes a JWTDecoder I can add to fake real auth
@Configuration
@Profile("fake-jwt")
class FakeJwtConfig {
@Bean
fun jwtDecoder(): JwtDecoder {
return JwtDecoder { token ->
if (token != "test.token") {
throw JwtException("Invalid token")
}
Jwt.withTokenValue(token)
.header("alg", "none")
.claim("sub", "user")
.issuedAt(Instant.now())
.expiresAt(Instant.now().plusSeconds(3600))
.build()
}
}
}
and configuration to handle authorization
@Profile("secure")
@Configuration
@EnableWebSocketSecurity
class WebSocketSecurityConfig {
@Bean
fun messageAuthorizationManager(
messages: MessageMatcherDelegatingAuthorizationManager.Builder
): AuthorizationManager<Message<*>> {
messages
// Next 2 lines are required for requests without auth.
// Remove these if all paths require auth
.simpTypeMatchers(SimpMessageType.CONNECT).permitAll()
.simpTypeMatchers(SimpMessageType.DISCONNECT).permitAll()
.simpDestMatchers("/app/status").permitAll()
.simpDestMatchers("/app/hello").authenticated()
.anyMessage().authenticated()
return messages.build()
}
}
I have a couple integration tests that seem to pass as expected, however, when I try to manually call the endpoint with something like
SEND
destination:/app/hello
Authorization: Bearer test.token
{"name":"test"}
It gets access denied...
Caused by: org.springframework.security.access.AccessDeniedException: Access Denied
The ones that do not require auth seem to work fine like
SEND
destination:/app/status
{"name":"test"}
work fine so I am confused why it works with test but I can't get it to work manually?
Full project here to be clear I am using hex-encoding etc...
In Postman, you'll need to set Authorization
inside headers instead of in the messages.
In your code, .oauth2ResourceServer { oauth2 -> oauth2.jwt { } }
expects a valid bearer token in the Authorization
header. The security context is propagated to WebSocket security as described here.
In your controller method, you can log the Authentication
like this to verify it's working.
@MessageMapping("/hello")
@SendTo("/topic/greetings")
fun greeting(
message: TestMessage,
authentication: Optional<Authentication>
): Greeting {
logger.info(message.toString())
authentication.ifPresent {
logger.info("Authentication: {}", it)
}
return Greeting("Hello, ${message.name}!")
}
Log Excerpt
Logs when sending two messages after connect. Note Securing GET /ws
followed by Set SecurityContextHolder to JwtAuthenticationToken
.
2025-02-06T14:59:27.354+01:00 DEBUG 3224140 --- [websocket] [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Securing GET /ws
2025-02-06T14:59:27.360+01:00 DEBUG 3224140 --- [websocket] [nio-8080-exec-1] o.s.s.o.s.r.a.JwtAuthenticationProvider : Authenticated token
2025-02-06T14:59:27.361+01:00 DEBUG 3224140 --- [websocket] [nio-8080-exec-1] .s.r.w.a.BearerTokenAuthenticationFilter : Set SecurityContextHolder to JwtAuthenticationToken [Principal=org.springframework.security.oauth2.jwt.Jwt@3d7ec21, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=127.0.0.1, SessionId=null], Granted Authorities=[]]
2025-02-06T14:59:27.363+01:00 DEBUG 3224140 --- [websocket] [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Secured GET /ws
2025-02-06T14:59:36.814+01:00 DEBUG 3224140 --- [websocket] [nio-8080-exec-2] .s.m.a.i.AuthorizationChannelInterceptor : Authorizing message send
2025-02-06T14:59:36.815+01:00 DEBUG 3224140 --- [websocket] [nio-8080-exec-2] .s.m.a.i.AuthorizationChannelInterceptor : Authorized message send
2025-02-06T14:59:36.867+01:00 INFO 3224140 --- [websocket] [nboundChannel-1] e.w.controller.WebSocketController : TestMessage(name=test)
2025-02-06T14:59:36.868+01:00 INFO 3224140 --- [websocket] [nboundChannel-1] e.w.controller.WebSocketController : Authentication: JwtAuthenticationToken [Principal=org.springframework.security.oauth2.jwt.Jwt@3d7ec21, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=127.0.0.1, SessionId=null], Granted Authorities=[]]
2025-02-06T14:59:40.626+01:00 DEBUG 3224140 --- [websocket] [nio-8080-exec-3] .s.m.a.i.AuthorizationChannelInterceptor : Authorizing message send
2025-02-06T14:59:40.626+01:00 DEBUG 3224140 --- [websocket] [nio-8080-exec-3] .s.m.a.i.AuthorizationChannelInterceptor : Authorized message send
2025-02-06T14:59:40.627+01:00 INFO 3224140 --- [websocket] [nboundChannel-4] e.w.controller.WebSocketController : TestMessage(name=test)
2025-02-06T14:59:40.627+01:00 INFO 3224140 --- [websocket] [nboundChannel-4] e.w.controller.WebSocketController : Authentication: JwtAuthenticationToken [Principal=org.springframework.security.oauth2.jwt.Jwt@3d7ec21, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=127.0.0.1, SessionId=null], Granted Authorities=[]]
Tested and verified with code from OP.