Spring中websocket相關的Bean有一個專門的Scope——websocket
,因此這在這些Bean當中是無法注入Scope為request
的各種Bean的。這也挺正常的,一個websocket可能會持續很長時間,request的各種Bean僅僅在握手的時候有用,一直不釋放也不是個辦法。
STOMP中用戶身份認證主要來自於握手的Http請求,具體來說來源於方法DefaultHandshakeHandler#getUser()
。各種Web的機制在這里也基本都適用。但當客戶端請求連接各種TOPIC,特別是各種動態的TOPIC時,鑒權的問題就有點不一樣了。Spring文檔中有這一句:
When a WebSocket handshake is made and a new WebSocket session is created, Spring’s WebSocket support automatically propagates the java.security.Principal from the HTTP request to the WebSocket session.
也就是說request中的Principal會自動轉到websocket中去,很好。然后我們可以利用ChannelInterceptor
來攔截訂閱請求。但這時候發現,Principal居然是null
:
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(message);
if (StompCommand.SUBSCRIBE.equals(headerAccessor.getCommand())) {
Principal principal = headerAccessor.getUser();
// principal is null here
}
return message;
}
原因倒是很簡單,SpringBoot的starter少了一個依賴:
compile 'org.springframework.security:spring-security-messaging'
缺少這個庫不會引發任何異常也沒有任何Log,但是會使得上面的代碼拿不到Principal。可以說各種AutoConfiguration還是挺容易留下這種坑的。SpringBoot的各種Starter用來很爽,就是很容易讓人知其然不知其所以然,太多的細節被隱藏了,如果程序工作得好倒還OK,一旦出現問題,連從哪里開始排查都不知道。像上面的情況,引入的相關的Starter,但是需要的特性還要格外的依賴,文檔中又沒有說明,是最容易踩到的大坑。