Authenticated Web Sockets Spring Boot 3

huangapple go评论64阅读模式
英文:

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&#39;re completely lost :(

Here more details on our configs and how we&#39;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(&quot;${spring.security.oauth2.client.registration.abc.clientId}&quot;)
var clientId: String? = null

@Value(&quot;${spring.security.oauth2.client.registration.abc.clientSecret}&quot;)
var clientSecret: String? = null

@Bean
fun customIntrospector(): OpaqueTokenIntrospector {
    return CustomAuthoritiesOpaqueTokenIntrospector(&quot;$tokenUri/introspect&quot;, 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(&quot;/**&quot;)
                .allowedOrigins(&quot;*&quot;)
                .allowedMethods(&quot;*&quot;)
        }
    }
}

}


WebSocketsConfig

@Configuration
@EnableWebSocketMessageBroker
@EnableWebSocketSecurity
internal class WebSocketConfig : WebSocketMessageBrokerConfigurer {
override fun configureMessageBroker(config: MessageBrokerRegistry) {
config
.enableSimpleBroker("/topic")
}

override fun registerStompEndpoints(registry: StompEndpointRegistry) {
    registry
        .addEndpoint(&quot;/ws&quot;)
        .setAllowedOriginPatterns(&quot;*&quot;)

}

@Configuration
open class WebSocketSecurityConfig {
    fun messageAuthorizationManager(messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager&lt;Message&lt;*&gt;&gt; {
        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 = () =&gt; {
    // Do something
  };

  client.connect(
    {},
    () =&gt; {
      socket = client;
      // Do something
    },
    // eslint-disable-next-line no-console
    (err: unknown) =&gt; 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 = &quot;csrfChannelInterceptor&quot;)
    ChannelInterceptor csrfChannelInterceptor() {
        return new ChannelInterceptor() {
            @Override
            public Message&lt;?&gt; preSend(Message&lt;?&gt; 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 = &quot;csrfChannelInterceptor&quot;)
    ChannelInterceptor csrfChannelInterceptor() {
        return new ChannelInterceptor() {
            @Override
            public Message&lt;?&gt; preSend(Message&lt;?&gt; message, MessageChannel channel) {
                return message;
            }
        };
    }

huangapple
  • 本文由 发表于 2023年2月24日 00:23:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/75547571.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定