Netty websocket服務(wù)器返回401,瀏覽器無(wú)響應(yīng)的解決策略
在使用Netty構(gòu)建WebSocket服務(wù)器并進(jìn)行Token驗(yàn)證時(shí),如果token無(wú)效,服務(wù)器返回401狀態(tài)碼并關(guān)閉連接,瀏覽器卻可能無(wú)任何反應(yīng)。本文分析此問(wèn)題并提供解決方案。
問(wèn)題描述
使用Netty開(kāi)發(fā)WebSocket服務(wù)器,需要驗(yàn)證token。token驗(yàn)證失敗時(shí),服務(wù)器返回401并關(guān)閉連接,但瀏覽器沒(méi)有響應(yīng),前端無(wú)法得知連接關(guān)閉原因。 服務(wù)器端代碼示例:
private void httpResponse401(ChannelHandlerContext ctx, FullHttpRequest request) { FullHttpResponse response = new DefaultFullHttpResponse(request.protocolVersion(), HttpResponseStatus.UNAUTHORIZED); response.headers().set(HttpHeaderNames.CONTENT_LENGTH, 0); ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); ReferenceCountUtil.release(request); }
前端JavaScript代碼:
var socket = new WebSocket("ws://127.0.0.1:18080/ws?token=xxxx");
服務(wù)器雖然返回401并關(guān)閉連接,但瀏覽器卻沒(méi)有任何反應(yīng)。
解決方案
瀏覽器無(wú)響應(yīng)是因?yàn)?01響應(yīng)在WebSocket握手階段被忽略了。 我們需要在握手階段或連接建立后進(jìn)行處理。
方案一:在握手階段返回401響應(yīng)
在WebSocket握手階段(HTTP請(qǐng)求階段),token驗(yàn)證失敗則直接返回HTTP 401響應(yīng),避免建立WebSocket連接。 修改服務(wù)器端代碼:
if (!validateToken(request)) { FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.UNAUTHORIZED); response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=utf-8"); response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); return; }
方案二:連接建立后發(fā)送自定義消息
如果需要在WebSocket連接建立后再處理401,可以在連接建立后立即發(fā)送自定義消息通知前端。 在Netty的WebSocketServerProtocolHandler中添加自定義處理器:
channel.pipeline().addLast(new WebSocketServerProtocolHandler("/ws") { @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt == WebSocketServerProtocolHandler.ServerHandshakeStateEvent.HANDSHAKE_COMPLETE) { if (!validateToken()) { ctx.writeAndFlush(new TextWebSocketFrame("401 Unauthorized")).addListener(ChannelFutureListener.CLOSE); } } super.userEventTriggered(ctx, evt); } });
前端JavaScript代碼需要監(jiān)聽(tīng)onmessage事件:
socket.onmessage = function(event) { if (event.data === "401 Unauthorized") { console.log("連接關(guān)閉,原因:401 Unauthorized"); socket.close(); } };
通過(guò)以上方法,瀏覽器就能正確處理401狀態(tài)碼,前端也能收到相應(yīng)的反饋信息,從而解決瀏覽器無(wú)響應(yīng)的問(wèn)題。 選擇哪種方案取決于具體的應(yīng)用場(chǎng)景和需求。