英文:
Authenticated Web Sockets Spring Boot 3
问题
以下是您提供的内容的翻译:
我和我的团队一直在尝试将项目迁移到Spring Boot 3和Spring Security 6,但在使用WebSockets时遇到了问题。如果我们移除`@EnableWebSocketSecurity`注解,一切都正常工作,但问题是只允许经过身份验证的消息。
当添加该注解时,前端出现了`ExecutorSubscribableChannel[clientInboundChannel]`错误,后端出现了`访问被拒绝`的错误。
我们还尝试按照官方文档[https://docs.spring.io/spring-security/reference/servlet/integrations/websocket.html#websocket-sameorigin-csrf]中的说明添加CSRF令牌,但唯一的结果是出现了`请求对象已被回收,不再与此外观相关`的错误消息。
迄今为止,我们将不胜感激任何帮助,因为我们感到完全迷茫:(
以下是有关我们的配置以及我们尝试创建连接的更多详细信息:
SecurityConfig
```kotlin
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
internal class OpaqueTokenSecurityConfig {
@Value("${spring.security.oauth2.client.provider.abc.token-uri}")
var tokenUri: String? = null
@Value("${spring.security.oauth2.client.registration.abc.clientId}")
var clientId: String? = null
@Value("${spring.security.oauth2.client.registration.abc.clientSecret}")
var clientSecret: String? = null
@Bean
fun customIntrospector(): OpaqueTokenIntrospector {
return CustomAuthoritiesOpaqueTokenIntrospector("$tokenUri/introspect", clientId, clientSecret)
}
@Bean
fun filterChain(http: HttpSecurity): SecurityFilterChain? {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy((SessionCreationPolicy.STATELESS))
.and()
.authorizeHttpRequests().anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
.and()
.oauth2ResourceServer()
.opaqueToken()
.introspector(customIntrospector())
return http.build()
}
@Bean
fun corsConfigurer(): WebMvcConfigurer {
return object : WebMvcConfigurer {
override fun addCorsMappings(registry: CorsRegistry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*")
}
}
}
}
WebSocketsConfig
@Configuration
@EnableWebSocketMessageBroker
@EnableWebSocketSecurity
internal class WebSocketConfig : WebSocketMessageBrokerConfigurer {
override fun configureMessageBroker(config: MessageBrokerRegistry) {
config
.enableSimpleBroker("/topic")
}
override fun registerStompEndpoints(registry: StompEndpointRegistry) {
registry
.addEndpoint("/ws")
.setAllowedOriginPatterns("*")
}
@Configuration
open class WebSocketSecurityConfig {
fun messageAuthorizationManager(messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<*>> {
messages
.anyMessage().authenticated()
return messages.build();
}
}
}
React客户端:
let socket: CompatClient;
const client = Stomp.over(() => new WebSocket(`ws://localhost:8081/ws?access_token=${keycloak.token}`));
client.onWebSocketClose = () => {
// 做一些操作
};
client.connect(
{},
() => {
socket = client;
// 做一些操作
},
// eslint-disable-next-line no-console
(err: unknown) => console.log(err),
);
<details>
<summary>英文:</summary>
my team and me have been trying to migrate to Spring boot 3 with Spring Security 6 but found problems with the web sockets. If we remove the `@EnableWebSocketSecurity` annotation everything works fine, but the point is to only allow authenticated messages.
When adding the annotation, all we get is a `ExecutorSubscribableChannel[clientInboundChannel]` error on the frontend and an `access denied` on the backend.
We also tried to add a csrf token as explained on the official docs [https://docs.spring.io/spring-security/reference/servlet/integrations/websocket.html#websocket-sameorigin-csrf] but the only thing we achieved was a `The request object has been recycled and is no longer associated with this facade` error message.
We would thank any help so far as we're completely lost :(
Here more details on our configs and how we're trying to create the connection:
SecurityConfig
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
internal class OpaqueTokenSecurityConfig {
@Value("${spring.security.oauth2.client.provider.abc.token-uri}")
var tokenUri: String? = null
@Value("${spring.security.oauth2.client.registration.abc.clientId}")
var clientId: String? = null
@Value("${spring.security.oauth2.client.registration.abc.clientSecret}")
var clientSecret: String? = null
@Bean
fun customIntrospector(): OpaqueTokenIntrospector {
return CustomAuthoritiesOpaqueTokenIntrospector("$tokenUri/introspect", clientId, clientSecret)
}
@Bean
fun filterChain(http: HttpSecurity): SecurityFilterChain? {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy((SessionCreationPolicy.STATELESS))
.and()
.authorizeHttpRequests().anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
.and()
.oauth2ResourceServer()
.opaqueToken()
.introspector(customIntrospector())
return http.build()
}
@Bean
fun corsConfigurer(): WebMvcConfigurer {
return object : WebMvcConfigurer {
override fun addCorsMappings(registry: CorsRegistry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*")
}
}
}
}
WebSocketsConfig
@Configuration
@EnableWebSocketMessageBroker
@EnableWebSocketSecurity
internal class WebSocketConfig : WebSocketMessageBrokerConfigurer {
override fun configureMessageBroker(config: MessageBrokerRegistry) {
config
.enableSimpleBroker("/topic")
}
override fun registerStompEndpoints(registry: StompEndpointRegistry) {
registry
.addEndpoint("/ws")
.setAllowedOriginPatterns("*")
}
@Configuration
open class WebSocketSecurityConfig {
fun messageAuthorizationManager(messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager<Message<*>> {
messages
.anyMessage().authenticated()
return messages.build();
}
}
}
Client on React:
let socket: CompatClient;
const client = Stomp.over(() => new WebSocket(ws://localhost:8081/ws?access_token=${keycloak.token}
));
client.onWebSocketClose = () => {
// Do something
};
client.connect(
{},
() => {
socket = client;
// Do something
},
// eslint-disable-next-line no-console
(err: unknown) => console.log(err),
);
</details>
# 答案1
**得分**: 1
使用`@EnableWebSocketSecurity`将在WebSocket上启用CSRF检查。如果你跟踪代码,你会发现这个CSRF拦截器在[这里](https://github.com/spring-projects/spring-security/blob/9ec3e80e15c709502633447beeef36f957e2c4e4/config/src/main/java/org/springframework/security/config/annotation/web/socket/WebSocketMessageBrokerSecurityConfiguration.java#L95-L99)进行了配置。因此,我通过添加一个空拦截器来禁用它:
```lang-java
@Bean(name = "csrfChannelInterceptor")
ChannelInterceptor csrfChannelInterceptor() {
return new ChannelInterceptor() {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
return message;
}
};
}
英文:
Using @EnableWebSocketSecurity
will enable CSRF checking on the WebSocket. If you trace the code, you will find this CSRF interceptor is configured here. Therefore, I disable it by adding an empty one:
@Bean(name = "csrfChannelInterceptor")
ChannelInterceptor csrfChannelInterceptor() {
return new ChannelInterceptor() {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
return message;
}
};
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论