《Spring Security》第十九章 WebSocket Security

第十九章 WebSocket Security

一、WebSocket Configuration

Spring Security 4.0 通过 Spring Messaging 抽象引入了对 WebSocket 的授权支持。如果需要使用 Java 配置授权,只需要扩展 AbstractSecurityWebSocketMessageBrokerConfigurer,并配置 MessageSecurityMetadataSourceRegistry。如:

/**
 * 1. 继承了 config 后,任何的入站 connect 消息都需要提供有效的 CSRF 令牌来实施同源策略
 * 2. SecurityContextHolder 由入站请求中的额 simpUser 头部属性中的用户填充
 */
@Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {

    protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
        // 任何以 "/user/" 开头的入站请求都需要 ROLE_USER 权限
        messages
            .simpDestMatchers("/user/*").authenticated();
    }
}

也可以使用 XML 的方式进行配置,如下:

<websocket-message-broker>
    <intercept-message pattern="/user/**" access="hasRole('USER')" />
</websocket-message-broker>

二、 WebSocket Authentication - WebSocket 认证

在创建与重连 WebSocket 连接时,在 HTTP 请求中发现的相同的认证信息。这意味着 HttpServletRequest 上的主体将会被移交给 websocket。

三、WebSocket Authorization - WebSocket 授权

授权配置实例如下:

@Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {

    @Override
    protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
        messages
            // 任何没有 destination 的消息都需要身份验证
            .nullDestMatcher().authenticated()
            // 任何人都可以订阅 /user/queue/errors
            .simpSubscribeDestMatchers("/user/queue/errors").permitAll()
            // 任何以 /app/ 开头的 destination 消息都需要 ROLE_USER 角色
            .simpDestMatchers("/app/**").hasRole("USER")
            // 任何以 /user/ 或 /topc/friends 开头的 SUBSCRIBE 订阅都需要 ROLE_USER 角色
            .simpSubscribeDestMatchers("/user/**","/topic/friends/*").hasRole("USER")
            // 任何其他 MESSAGE 或 SUBSCRIBE 类型的消息都将被拒绝
            .simpTypeMatchers(MESSAGE, SUBSCRIBE).denyAll()
            // 任何其他消息都将被拒绝(这样做,可以确保不会遗漏任何消息)
            .anyMessage().denyAll();
    }
}

四、WebSocket Authorization Notes

1. WebSocket Authorization on Message Types

WebSocket 授权说明,一般情况下,我们只希望客户端订阅 “/topic/system/notifications”,而不希望客户端向该 destination 发送消息。

如果我们允许客户端向 “/topic/system/notifications” 发送消息,那么客户端就可以直接向所有的订阅端点发送消息,并模拟系统。

2. WebSocket Authorization on Destinations

WebSocket 订阅的授权。以聊天应用为例,我们希望客户端监听 “/user/queue”,它将转换为 “/queue/user/message-“,但是我们不希望客户机能够监听 “/queue/*”,这样客户端就能查看每个用户的消息了。

一般情况下,应用程序通常拒绝向以代理前缀(如”/topic”或”/queue”) 开头的消息发送任何订阅。

3. Outbound Messages

Spring 包含了一个名为 “Flow of Message(消息流)” 的部分,描述了消息如何在系统中流动。

值得注意的是,Spring Security 只保护 clientInboundChannel,而不保护 clientOutboundChannel。

这是因为每一条进入的信息,通常对应着更多发出的信息。因此为了性能考虑,Spring Security 鼓励保护端点的订阅。

五、Enforcing Same Origin Policy 同源策略

假定在用户浏览器中,访问了某个网站并进行了身份验证,此时用户另打开一个标签页并访问了另一个网址,那么同源策略确保了网站二不能读写数据到网站一。

而 websocket 与 SocketJS,都绕过了同源策略,因此开发人员需要显式地保护自己的应用不受外域攻击。

1. Adding CSRF to Stomp Headers

默认情况下,Spring Security 要求在任何 connect 消息中使用 CSRF 令牌,这确保只有访问 CSRF 令牌的站点才能连接。

但是 SockJS 不允许这些,因此我们必须在 Stomp 头文件中包含 token。

2. Disable CSRF within WebSockets

如果允许其他域访问站点,可以禁用 SpringSecurity 的保护,配置如下:

@Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {

    @Override
    protected boolean smaeOriginDesabled() {
        return true;
    }
}
文章作者: koral
文章链接: http://luokaiii.github.io/2019/07/24/读书笔记/《SpringSecurity》/19.WebSocket/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自