问题描述
我在micronaut中定义了以下websocket服务器:
@ServerWebSocket("/v1/ws/socket")
@Secured(SecurityRule.IS_AUTHENTICATED)
@requiredArgsConstructor
@Slf4j
public class MyWebSocket {
@Onopen
public void onopenSocket(WebSocketSession session) {
String agentRef = session.getUserPrincipal().get().getName();
log.info("opened websocket for: {}",agentRef);
}
@OnClose
public void onClose(CloseReason closeReason) {
log.info("closed websocket websocket with reason: {}",closeReason);
}
@OnMessage
public void onMessage(String message) {
log.info("received message in websocket: {}",message);
}
@OnError
public void onError(Throwable error) {
log.error("an error occured in websocket",error);
}
}
我的应用程序具有JWT令牌身份验证,在我的单元测试中,我可以连接到套接字,而不会出现以下问题:
@Inject
@Client("/")
RxWebSocketClient webSocketClient1
@Shared
def bearer = 'example jwt'
def "client can authenticate and connect to the websocket"() {
given:
def request = HttpRequest.GET("/v1/ws/").bearerAuth(bearer)
def MyWebSocketClient client =
webSocketClient1.connect(MyWebSocketClient,request)
.blockingFirst()
expect:
client.session.isopen()
cleanup:
client.session.close(CloseReason.norMAL)
}
在测试中,客户端已正确认证,并且一切正常。但是我无法使用rxjs套接字库对前端代码中的websocket进行身份验证。我找不到任何有关Micronaut一般如何处理Websocket安全的文档。你对我有什么提示吗?
解决方法
在micronaut中,您可以实现自己的 TokenReader ,以从查询参数获取令牌。
@Singleton
class AccessTokenReader implements TokenReader {
@Override
public Optional<String> findToken(HttpRequest<?> request) {
return request.getParameters().get("access_token",String.class);
}
}
如果micronaut在标头中找不到令牌,它将使用您的实现。 您要做的就是将令牌放入查询参数:
http://localhost:8080/v1/ws?access_token=xxxxxxxxx
在JavaScript中,它看起来像这样:
new WebSocket("ws://localhost:8080/v1/ws?access_token=xxxxxxxxx")
,
对于感兴趣的任何人,经过一定程度的研究,websocket确实支持身份验证-有一个初始HTTP调用,该响应会导致响应switching protocols
,然后打开套接字连接。
该想法是在初始HTTP调用中对用户进行身份验证-照常在标头中传递身份验证。但是,问题在于,要么Websocket的前端库不支持在该http调用上传递此标头,要么浏览器不允许它-我不是真正的前端人员,但这就是要旨。
我最终通过生成前端必须通过rest api生成的“一次性”令牌来解决它,然后通过查询将其传递给websocket。我知道用户已通过身份验证,因为用于生成令牌的端点是安全的。