成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

WebSocket 自定義安全校驗(yàn)優(yōu)化實(shí)踐

開(kāi)發(fā) 前端
在以Spring Boot與Vue搭建的應(yīng)用體系里,WebSocket作為實(shí)現(xiàn)前后端實(shí)時(shí)交互的得力助手,被廣泛運(yùn)用。然而隨著網(wǎng)絡(luò)安全形勢(shì)日益嚴(yán)峻,為WebSocket交互筑牢安全防線變得至關(guān)重要。

前言

在以Spring Boot與Vue搭建的應(yīng)用體系里,WebSocket作為實(shí)現(xiàn)前后端實(shí)時(shí)交互的得力助手,被廣泛運(yùn)用。然而隨著網(wǎng)絡(luò)安全形勢(shì)日益嚴(yán)峻,為WebSocket交互筑牢安全防線變得至關(guān)重要。

實(shí)現(xiàn)

傳統(tǒng)

@Slf4j
@Component
@ServerEndpoint("/websocket/link/{userId}")
public class OldWebSocketService {
    // 用于存儲(chǔ)在線用戶的會(huì)話,使用ConcurrentHashMap確保線程安全
    private static final Map<String, Session> onlineSessions = new ConcurrentHashMap<>();

    @OnOpen
    public void handleOpen(Session session, @PathParam("userId") String userId) {
        onlineSessions.put(userId, session);
        log.info("用戶ID為 {} 的用戶已連接,當(dāng)前在線用戶數(shù): {}", userId, onlineSessions.size());
        broadcastMessage("系統(tǒng)提示:有新用戶加入");
    }

    @OnMessage
    public void handleMessage(String message, Session session, @PathParam("userId") String userId) {
        log.info("服務(wù)端收到用戶ID為 {} 的消息: {}", userId, message);
        JSONObject jsonMessage = JSON.parseObject(message);
        String targetUserId = jsonMessage.getString("to");
        String content = jsonMessage.getString("content");

        Session targetSession = onlineSessions.get(targetUserId);
        if (targetSession != null) {
            JSONObject responseMessage = new JSONObject();
            responseMessage.put("from", userId);
            responseMessage.put("content", content);
            sendMessage(responseMessage.toJSONString(), targetSession);
            log.info("向用戶ID為 {} 發(fā)送消息: {}", targetUserId, responseMessage.toJSONString());
        } else {
            log.info("未能找到用戶ID為 {} 的會(huì)話,消息發(fā)送失敗", targetUserId);
        }
    }

    private void sendMessage(String message, Session targetSession) {
        try {
            log.info("服務(wù)端向客戶端[{}]發(fā)送消息: {}", targetSession.getId(), message);
            targetSession.getBasicRemote().sendText(message);
        } catch (Exception e) {
            log.error("服務(wù)端向客戶端發(fā)送消息失敗", e);
        }
    }

    private void broadcastMessage(String message) {
        for (Session session : onlineSessions.values()) {
            sendMessage(message, session);
        }
    }

    @OnClose
    public void handleClose(Session session, @PathParam("userId") String userId) {
        onlineSessions.remove(userId);
        log.info("用戶ID為 {} 的連接已關(guān)閉,當(dāng)前在線用戶數(shù): {}", userId, onlineSessions.size());
    }
}

打造安全加固的 WebSocket 體系

實(shí)現(xiàn)流程

  1. 客戶端發(fā)起連接:客戶端向/secure-websocket地址發(fā)起WebSocket連接請(qǐng)求,在請(qǐng)求頭中攜帶Authorization(即token)和Unique-User-Key(用戶唯一標(biāo)識(shí))。
  2. 攔截器校驗(yàn):服務(wù)端的SecurityInterceptor攔截請(qǐng)求,獲取并校驗(yàn)token。若token無(wú)效,阻止握手;若有效,則將用戶唯一標(biāo)識(shí)存入attributes。
  3. 連接建立:若攔截器允許握手,連接成功建立。EnhancedWebSocketHandler的afterConnectionEstablished方法被調(diào)用,獲取用戶唯一標(biāo)識(shí)并存儲(chǔ)會(huì)話。
  4. 消息交互:客戶端和服務(wù)端進(jìn)行消息收發(fā)。EnhancedWebSocketHandler的handleMessage方法處理消息前,先校驗(yàn)消息來(lái)源的用戶唯一標(biāo)識(shí)是否合法。
  5. 連接關(guān)閉:連接關(guān)閉時(shí),EnhancedWebSocketHandler的afterConnectionClosed方法被調(diào)用,移除對(duì)應(yīng)會(huì)話。

自定義 WebSocket 處理器

@Slf4j
@Component
public class EnhancedWebSocketHandler implements WebSocketHandler {
    // 存儲(chǔ)用戶標(biāo)識(shí)與會(huì)話的映射關(guān)系,保證線程安全
    private static final Map<String, WebSocketSession> userSessionMap = new ConcurrentHashMap<>();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        String userKey = (String) session.getAttributes().get("uniqueUserKey");
        session.sendMessage(new TextMessage("用戶:"+userKey+" 認(rèn)證成功"));

        log.info("WebSocket連接已建立,用戶唯一標(biāo)識(shí): {}, 會(huì)話ID: {}", userKey, session.getId());
        userSessionMap.put(userKey, session);
        log.info("新用戶連接,當(dāng)前在線用戶數(shù): {}", userSessionMap.size());
    }

    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        JSONObject json = JSONObject.parseObject((String) message.getPayload());
        if(!userSessionMap.containsKey(json.getString("to"))){
            session.sendMessage(new TextMessage("接收用戶不存在!!!"));
            return;
        }
        String userKey = (String) session.getAttributes().get("uniqueUserKey");
        if (!userSessionMap.containsKey(userKey)) {
            session.sendMessage(new TextMessage("發(fā)送用戶不存在!!!"));
            return;
        }
        session.sendMessage(new TextMessage("收到 over"));
        log.info("消息接收成功,內(nèi)容: {}", message.getPayload());
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        String userKey = (String) session.getAttributes().get("uniqueUserKey");
        if (userSessionMap.containsKey(userKey)) {
            log.error("WebSocket傳輸出現(xiàn)錯(cuò)誤,用戶標(biāo)識(shí): {}, 錯(cuò)誤信息: {}", userKey, exception.getMessage());
        }
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
        String userKey = (String) session.getAttributes().get("uniqueUserKey");
        log.info("WebSocket連接已關(guān)閉,會(huì)話ID: {}, 關(guān)閉狀態(tài): {}", session.getId(), closeStatus);
        userSessionMap.remove(userKey);
    }

    @Override
    public boolean supportsPartialMessages() {
        returntrue;
    }

    public void sendMessage(String message, WebSocketSession targetSession) {
        try {
            log.info("服務(wù)端向客戶端[{}]發(fā)送消息: {}", targetSession.getId(), message);
            targetSession.sendMessage(new TextMessage(message));
        } catch (Exception e) {
            log.error("服務(wù)端向客戶端發(fā)送消息失敗", e);
        }
    }

    public void broadcastMessage(String message) {
        for (WebSocketSession session : userSessionMap.values()) {
            sendMessage(message, session);
        }
    }
}

自定義 WebSocket 攔截器

@Slf4j
@Component
public class SecurityInterceptor implements HandshakeInterceptor {
    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler webSocketHandler, Map<String, Object> attributes) throws Exception {
        // 獲取 HttpServletRequest 對(duì)象
        HttpServletRequest rs=((ServletServerHttpRequest) request).getServletRequest();
        String token = rs.getParameter("Authorization");
        log.info("攔截器獲取到的令牌: {}", token);
        if (token == null ||!isValidToken(token)) {
            log.warn("無(wú)效的令牌,拒絕WebSocket連接");
            returnfalse;
        }
        String userKey = rs.getParameter("UniqueUserKey");
        attributes.put("uniqueUserKey", userKey);
        returntrue;
    }

    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler webSocketHandler, Exception exception) {
        // 可在此處添加握手成功后的處理邏輯
    }

    private boolean isValidToken(String token) {
        // 實(shí)際應(yīng)用中應(yīng)包含復(fù)雜的令牌驗(yàn)證邏輯,如JWT驗(yàn)證
        // 此處僅為示例,簡(jiǎn)單判斷令牌是否為"validToken"
        return"1234".equals(token);
    }
}

WebSocket 配置類

@Configuration
@EnableWebSocket
public class WebSocketSecurityConfig implements WebSocketConfigurer {
    private final EnhancedWebSocketHandler enhancedWebSocketHandler;
    private final SecurityInterceptor securityInterceptor;

    public WebSocketSecurityConfig(EnhancedWebSocketHandler enhancedWebSocketHandler, SecurityInterceptor securityInterceptor) {
        this.enhancedWebSocketHandler = enhancedWebSocketHandler;
        this.securityInterceptor = securityInterceptor;
    }

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(enhancedWebSocketHandler, "/secure-websocket")
               .setAllowedOrigins("*")
               .addInterceptors(securityInterceptor);
    }

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

示例頁(yè)面

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebSocket 認(rèn)證交互頁(yè)面</title>
    <link  rel="stylesheet" >
    <style>
        body {
            font-family: Arial, sans-serif;
        }

        #authSection {
            margin-bottom: 10px;
        }

        #tokenInput,
        #userKeyInput {
            width: 200px;
            padding: 8px;
            margin-right: 10px;
        }

        #authButton {
            padding: 8px 16px;
        }

        #messageInput {
            width: 300px;
            padding: 8px;
            margin-right: 10px;
        }

        #targetUserInput {
            width: 200px;
            padding: 8px;
            margin-right: 10px;
        }

        #sendButton {
            padding: 8px 16px;
        }

        #messageList {
            list-style-type: none;
            padding: 0;
        }

        #messageList li {
            margin: 8px 0;
            border: 1px solid #ccc;
            padding: 8px;
            border-radius: 4px;
        }
    </style>
</head>

<body>
<h2>WebSocket 認(rèn)證交互頁(yè)面</h2>
<div id="authSection">
    <label for="tokenInput">輸入認(rèn)證 Token:</label>
    <input type="text" id="tokenInput" placeholder="請(qǐng)輸入認(rèn)證 Token">
    <label for="userKeyInput">輸入用戶唯一標(biāo)識(shí):</label>
    <input type="text" id="userKeyInput" placeholder="請(qǐng)輸入用戶唯一標(biāo)識(shí)">
    <button id="authButton">認(rèn)證并連接</button>
</div>
<input type="text" id="messageInput" placeholder="請(qǐng)輸入要發(fā)送的消息">
<input type="text" id="targetUserInput" placeholder="請(qǐng)輸入接收消息的用戶標(biāo)識(shí)">
<button id="sendButton">發(fā)送消息</button>
<ul id="messageList"></ul>
<script>
    let socket;

    document.getElementById('authButton').addEventListener('click', function () {
        const token = document.getElementById('tokenInput').value;
        const userKey = document.getElementById('userKeyInput').value;
        if (token.trim() === '' || userKey.trim() === '') {
            console.error('Token 或用戶唯一標(biāo)識(shí)不能為空');
            return;
        }

        const socketUrl = 'ws://localhost:8080/secure-websocket?Authorization='+token+'&UniqueUserKey='+userKey ;
        socket = new WebSocket(socketUrl);

        socket.onopen = function () {
             console.log('WebSocket 連接已打開(kāi)');
        };

        socket.onmessage = function (event) {
            const messageItem = document.createElement('li');
            messageItem.textContent = event.data;
            document.getElementById('messageList').appendChild(messageItem);
        };

        socket.onclose = function () {
            console.log('WebSocket 連接已關(guān)閉');
        };

        socket.onerror = function (error) {
            console.error('WebSocket 發(fā)生錯(cuò)誤:', error);
        };
    });

    document.getElementById('sendButton').addEventListener('click', function () {
        if (!socket || socket.readyState!== WebSocket.OPEN) {
            console.error('WebSocket 連接未建立或已關(guān)閉');
            return;
        }
        const message = document.getElementById('messageInput').value;
        const targetUser = document.getElementById('targetUserInput').value;
        if (message.trim() === '' || targetUser.trim() === '') {
            return;
        }
        const messageObj = {
            to: targetUser,
            content: message
        };
        socket.send(JSON.stringify(messageObj));
        document.getElementById('messageInput').value = '';
        document.getElementById('targetUserInput').value = '';
    });
</script>
</body>

</html>

測(cè)試

圖片圖片

責(zé)任編輯:武曉燕 來(lái)源: 一安未來(lái)
相關(guān)推薦

2017-05-19 10:03:31

AndroidBaseAdapter實(shí)踐

2023-12-21 09:00:21

函數(shù)React 組件useEffect

2017-05-18 12:36:16

android萬(wàn)能適配器列表視圖

2025-01-22 11:10:34

2022-04-01 15:59:22

SQLPostgreSQL審計(jì)

2010-08-12 09:45:33

jQuery自定義事件

2023-06-27 15:02:47

2023-06-28 08:05:46

場(chǎng)景vue3自定義

2015-02-12 15:33:43

微信SDK

2015-02-12 15:38:26

微信SDK

2009-07-06 13:49:29

2012-02-29 09:14:45

ibmdw

2012-03-06 09:19:56

ibmdw

2016-12-26 15:25:59

Android自定義View

2016-11-16 21:55:55

源碼分析自定義view androi

2011-06-23 10:49:13

Qt 自定義信號(hào)

2022-11-10 07:53:54

Spring參數(shù)校驗(yàn)

2023-10-30 16:14:44

Metrics SD數(shù)據(jù)庫(kù)

2011-09-08 13:56:41

ASP.NET性能

2009-07-06 16:59:26

JSP自定義標(biāo)簽
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 久久久精品一区二区 | 中文字幕 在线观看 | 精品国产乱码久久久久久果冻传媒 | 欧美一区二区三区在线观看 | 中文字幕一区二区三区四区五区 | 国产一区二区三区欧美 | 成人欧美一区二区三区在线播放 | 毛片免费观看 | 久久爱黑人激情av摘花 | www日韩| 中文字幕亚洲欧美日韩在线不卡 | 日本精品一区二区三区四区 | 久久久久免费精品国产小说色大师 | 自拍在线 | 久久毛片 | 亚洲一区播放 | 男人天堂久久 | 亚洲综合久久久 | 欧美 日韩 中文 | 蜜桃视频一区二区三区 | 日本高清精品 | 色资源在线视频 | 天天操操| 欧美嘿咻 | 欧美日日 | 本道综合精品 | 久久久www成人免费精品张筱雨 | 欧美在线一区二区三区 | 成人在线视频免费看 | 久久www免费视频 | 天天色影视综合 | 国产视频中文字幕在线观看 | 免费观看一级毛片 | 乱码av午夜噜噜噜噜动漫 | 亚洲国产成人精品女人久久久 | 国产精品自拍视频网站 | 国产精品一区二区三 | 日韩免费1区二区电影 | 成人免费看 | 日韩国产中文字幕 | 国产免费一区二区 |