第十九章 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-
一般情况下,应用程序通常拒绝向以代理前缀(如”/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;
}
}