springboot整合websocket實現登錄擠退現象


在項目期間遇到了同一個賬號不能在不同的地方同時登錄的情況,解決方法用到了websocket。

關於websocket的原理網上有很多,我這里就不寫了,推薦博客:

https://www.cnblogs.com/myzhibie/p/4470065.html

websocket理清原理:https://zhuanlan.zhihu.com/p/95622141

這里我主要記錄一下websocket來實現的登錄擠退的功能

一:實現的思想

1.我的思路是這樣的,在登錄的時候要去后台驗證賬號密碼的正確性,如果這個都不正確那就別說了。

2.當賬號和密碼正確時,在session里面存儲一下該用戶信息,后台返回給前端一個標准,表示賬號和密碼正確,然后前端通過js來建立websocket的連接

后台會接收這個連接,然后在這個連接中取出該連接服務器的session,通過session里面存儲的用戶id來判斷靜態變量websocket

list里面是否含有該用戶id的websocket(畢竟用戶id為唯一標識)。

3.如果含有,則說明該用戶已經在登錄的狀態。所以通過后台的websocket對象來發送消息,告知前端js的websocket說用戶已經登錄了。

4.如果不含有,則說明該賬號目前不處於登錄狀態,就存放到靜態變量List<Websocket>里面。並發送消息到前台說明登錄成功。

以上為最基本的思想。但是問題來了。如何實現?同時,如何在websocket獲得該次連接服務器的HttpSession對象?

慢慢來解決。這里默認該用戶賬號密碼正確,從js發送websocket的連接開始。

maven依賴:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

 

 

 

二:實現

  1.js發送websocket的連接

  

function onenSocket(){
   /*newsinfo2為項目的根目錄,websocket為請求地址,后台通過注解接收*/
var socket = new WebSocket("ws://localhost:8080/newsinfo2/webSocket/"); if(typeof(socket) == undefined){ alert("您的瀏覽器不支持webSocket,請換一個瀏覽器..."); return ; }
    /*websocket接收消息的函數*/ socket.onmessage
= function(msg){ if(msg == "已登錄"){ alert("您的賬號已在另一個地方嘗試登錄,如果不是您知曉的情況,請及時修改密碼..."); }else if(msg == "登錄成功"){ location.href="../index/index.html"; }else if(msg == "修改密碼"){ alert("您賬號的密碼已經被修改!如果不是你自己知曉的情況,請及時修改密碼..."); location.href="../login/login.html"; } } //socket打開時的方法 socket.onopen = function(){ } //socket關閉時的方法 socket.onclose = function(){ } //socket出錯時的方法 socket.onerror = function(){ } /*//在頁面加載時自動斷開鏈接,這樣就不會異常斷開鏈接,后台不會報錯誤 $(document).ready(function(){ socket.close(); });*/ }

該js發送請求后,后台接收如下:

  2.后台websocket的接收

@ServerEndpoint(value = "/webSocket/")
public class WebSocketServer {

WebSocketServer為自創的類。通過這個注解,這個類會有一些自帶的方法:

  onopen():連接時需要調用的方法

  onError():出現錯誤時執行的方法

  onClose():關閉連接時調用的方法

該類中必須要自定義一個靜態變量:

  

//用於存儲webSocketServer

public static CopyOnWriteArraySet<WebSocketServer> webSocketServerSet = new CopyOnWriteArraySet<WebSocketServer>();

 這個框架就算是建立了,接下來是一些縫縫補補的工作。

要完整的將我的webSocketServer呈現,那還需要獲得httpsession對象。獲得對象的方法和思想請看下面的博客:

https://www.cnblogs.com/zhuxiaojie/p/6238826.html

我的WebSocket整體呈現:

package news.webSocket;

import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.servlet.http.HttpSession;
import javax.websocket.EndpointConfig;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;

import news.bean.UserInfo;
import news.config.Configuretor;
import news.utils.LogerUtils;
import news.utils.StaticValue;

/**
 * webSocketServer類,用於處理登錄擠退現象,
 * 思路:登錄時,要判斷該賬號是否已創建一個webSocket對象存儲起來了,根據這個判斷的結果來進行下一步動作
 * @author 徐金仁
 */
@ServerEndpoint(value = "/webSocket/" , configurator = Configuretor.class)
public class WebSocketServer {
    //用於存儲webSocketServer
    public static CopyOnWriteArraySet<WebSocketServer> webSocketServerSet = new CopyOnWriteArraySet<WebSocketServer>();
    private Session session;  //與某個客戶端連接的會話,該session是屬於WebSocket的session,不屬於HttpSession
    private String sid; //用戶的編號
    private Logger log = LogerUtils.getLogger(this.getClass());
    @Autowired
    private HttpSession httpSession_;
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((session == null) ? 0 : session.hashCode());
        result = prime * result + ((httpSession_ == null) ? 0 : httpSession_.hashCode());
        result = prime * result + ((sid == null) ? 0 : sid.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        WebSocketServer other = (WebSocketServer) obj;
        if (session == null) {
            if (other.session != null)
                return false;
        } else if (!session.equals(other.session))
            return false;
        if (httpSession_ == null) {
            if (other.httpSession_ != null)
                return false;
        } else if (!httpSession_.equals(other.httpSession_))
            return false;
        if (sid == null) {
            if (other.sid != null)
                return false;
        } else if (!sid.equals(other.sid))
            return false;
        return true;
    }

    public static CopyOnWriteArraySet<WebSocketServer> getWebSocketServerSet() {
        return webSocketServerSet;
    }

    public static void setWebSocketServerSet(CopyOnWriteArraySet<WebSocketServer> webSocketServerSet) {
        WebSocketServer.webSocketServerSet = webSocketServerSet;
    }

    public String getSid() {
        return sid;
    }

    public void setSid(String sid) {
        this.sid = sid;
    }

    public HttpSession gethttpSession_() {
        return httpSession_;
    }

    public void sethttpSession_(HttpSession httpSession_) {
        this.httpSession_ = httpSession_;
    }

    public void setSession(Session session) {
        this.session = session;
    }
    
    /**
     * 獲取HttpSession
     * @return
     */
    public HttpSession getHttpSession(){
        return this.httpSession_;
    }
    
    /**
     * 獲取session
     * @return
     */
    public Session getSession(){
        return this.session;
    }
    
    /**
     * 連接的時候需要調用的方法
     * @throws IOException 
     */
    @OnOpen
    public void onOpen(Session session,  EndpointConfig config) throws IOException{
        HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
        this.session = session;
        this.httpSession_ = httpSession;
        System.out.println("鏈接中..." + httpSession_.getId());
        //StaticValue為自定義的存放key值的類,里面都是一些常量
        Object obj =  (this.httpSession_.getAttribute(StaticValue.CURRENT_USER));
        if(obj != null){ //說明還鏈接中
            this.sid = String.valueOf(((UserInfo)obj).getUid());
            log.info(this.sid + "正在鏈接中...");
            if(!webSocketServerSet.contains(this)){
                webSocketServerSet.add(this); //將連接到的添加進入set里面
            }
        }else{ //說明不鏈接了
            //等會在寫
        }
        /*this.sendMessage("連接成功!");*/
    }
    /**
     * 發送消息的方法
     * @param string
     * @throws IOException 
     */
    public void sendMessage(String msg) throws IOException  {
        this.session.getBasicRemote().sendText(msg);
    }
    
    /**
     * 出現錯誤的方法
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session , Throwable error){
        log.error( this.sid + "websocket出錯斷開鏈接");
    }
    /**
     * 當連接斷開時,調用的方法
     */
    @OnClose
    public void onClose(){
        webSocketServerSet.remove(this);
    }
    
    /**
     * 根據sid查詢webSocket
     * @param sid
     * @return
     */
    public static WebSocketServer getWebSocket(String sid){
        for(WebSocketServer w : webSocketServerSet){
            if(sid.equals(w.sid)){
                return w;
            }
        }
        return null;
    }
}

controller層:

@RequestMapping("login")
    public UserInfo login(String uname, String upwd, HttpSession session) throws IOException{
        int result = 0;
        UserInfo userInfo = new UserInfo();
        userInfo.setUname(uname);
        userInfo.setUpwd(upwd);
        UserInfo us = null;
        us = loginService.login(userInfo);
        if(us == null){
            us = new UserInfo();
            us.setUid(-1); //表示賬號或密碼不對
        }else{//如果查尋到賬號和密碼都沒有錯誤,則要判斷是否已經被登錄了,
            WebSocketServer wws = WebSocketServer.getWebSocket(String.valueOf(us.getUid()));
            if(wws != null){ //如果有
                wws.sendMessage("已登錄");
                us.setUid(-2); //表示已登錄
            }else{//表示暫時沒有人登錄,您是第一個,要將信息存儲一下
                session.setAttribute("userInfo", us);
                session.setAttribute(StaticValue.CURRENT_USER, us);
                System.out.println("session的id:" + session.getId());
            }
        }
        System.out.println(us);
        return us;

 

這里還有幾個坑,一個是如果就是登陸成功后,頁面一刷新,websocket就會出異常斷開,這里沒有什么好的辦法,只有每次刷新或者跳轉頁面的之后,都要重新鏈接。

還有一個是localhost訪問的情況和127.0.0.1訪問的情況下是不同的。如果你在js中鏈接使用127.0.0.1,而項目運行后在瀏覽器地址上顯示的是localhost的話,那么獲得的HttpSession並不是同一個對象。這樣的話會導致程序員的判斷出現錯誤。解決的辦法是同一使用127.0.0.1或者是localhost。至於為什么會出現這種不同,請查看下面:

localhost 127.0.0.1和本機ip三者的區別

localhost 
不聯網 
不使用網卡,不受防火牆和網卡限制 
本機訪問 

127.0.0.1 
不聯網 
網卡傳輸,受防火牆和網卡限制 
本機訪問 

本機IP 
聯網 
網卡傳輸 ,受防火牆和網卡限制 
本機或外部訪問

以上三者區別知識的來源:

https://blog.csdn.net/qq_35101027/article/details/80745664

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM