WebSocket + Redis簡單快速實現(xiàn)Web網(wǎng)站單設(shè)備登錄功能

大家好,我是小悟

1、寫在前面的話

生活中,我們在使用一些APP的時候,有過一種體驗,就是在A手機上登錄賬號,因為某些原因需要在B手機上登錄,然后就會在A手機上看到類似"該賬號在其他設(shè)備登錄"的提示,像下面這樣:

image.png

這種方式叫單設(shè)備登錄,作用很明顯,就是為了保護用戶賬號安全,今天我們不說手機APP,我們來說說PC Web網(wǎng)站如何簡單快速實現(xiàn)這種效果。本篇文章重點是實現(xiàn)單設(shè)備登錄,內(nèi)容未涉及WebSocket + Redis的概念和使用方法。限于本人經(jīng)驗,如有錯誤,歡迎指正。

2、概念

簡單的給”單設(shè)備登錄“定義一下,就是只能在一個設(shè)備上登錄,若同時在其他設(shè)備登錄,先前登錄的用戶會被提醒:該賬戶在其他設(shè)備登錄。例如微信,在一臺手機登錄中,同時拿另一臺手機登錄該賬戶,之前那部手機的賬戶會被擠下線。

3、思路

使用此方案的前提是要保證登錄賬號沒有重復(fù)

在socket里面創(chuàng)建兩個Map,sessionPool和sessionIds,分別用來存放客戶端會話池和客戶端會話標(biāo)記

用戶登錄賬號成功,進入應(yīng)用首頁,和服務(wù)端建立socket連接,將賬號+"_"+UUID格式的字符串作為state參數(shù)的值

在OnOpen連接時,以state為key,當(dāng)前session為value存入sessionPool,以sessionId為key,state為value存入sessionIds

以"_"分隔state成數(shù)組,取第一個元素即獲取到當(dāng)前登錄的賬號

在緩存(Redis)里模糊查詢含有該賬號的key集合,如果存在,那么就取出對應(yīng)的value值,其實這個value存的就是首先登陸這個賬號的那個state,就可以根據(jù)這個state給先登錄的賬號設(shè)備推送消息并做logout的操作,并清除緩存

把當(dāng)前登錄的state作為key和value存入緩存,失效時間設(shè)置與否都可以,如果設(shè)置的話需超過登錄態(tài)失效的時長

4、代碼實現(xiàn)

這里貼上幾段核心代碼

后臺WebSocket-On0pen,切記如果設(shè)置緩存失效時間的話需超過登錄態(tài)失效的時長

/**
 * 連接時觸發(fā)
 * @param state
 * @param session
 */
@OnOpen
public void onOpen(@PathParam(value = "state")String state,Session session) {
    this.session = session;
    sessionPool.put(state, session);
    sessionIds.put(session.getId(), state);     
    String[] arry = state.split("_");
    Set<String> keys = redisService.keys(arry[0] + "*");
    if (keys != null) {
        List<String> list = redisService.multiGet(keys);
        for (String value : list) {
            sendMessage("您的賬號于"+ DateUtils.pageformat(DateUtils.getCurrentTime()) + "在另一臺設(shè)備登錄,如果這不是您的操作,那么您的登錄密碼已泄露,請盡快修改",value);
            redisService.delete(value);
        }
    }
    redisService.set(state,state,7200);
}

/**
 * 自定義發(fā)送消息的方法
 * @param message
 * @param state
 */
public static void sendMessage(String message,String state) {
    Session session = sessionPool.get(state);
    if (session != null) {
        try {
            session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

查看緩存

image.png

前端js連接WebSocket方法

function getUUID() {
return 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
    var r = Math.random() * 16 | 0,
        v = (c === 'x' ? r : (r & 0x3 | 0x8));
    return v.toString(16);
});
}

function loginSocket(userName) {
var websocket = null;
if ('WebSocket' in window) {
    websocket = new WebSocket("ws://localhost:8080/mobile/socketServer/"+userName+"_"+getUUID());
} else {
    layer.alert('當(dāng)前瀏覽器 不支持 websocket')
}
//連接成功建立的回調(diào)方法
websocket.onopen = function () {
    console.log('websocket連接成功');
};
//連接發(fā)生錯誤的回調(diào)方法
websocket.onerror = function () {
    console.log('websocket連接發(fā)生錯誤');
};
//接收到消息的回調(diào)方法
websocket.onmessage = function (event) {
    $.getJSON("logout", function(r){
        console.log('logout:'+event.data);
    });
    layer.confirm(event.data, {
        btn: ['確定'] //按鈕
    }, function(){
        location.href = 'login.html';
    });
};
//連接關(guān)閉的回調(diào)方法
websocket.onclose = function () {
    console.log("websocket連接關(guān)閉");
};
//監(jiān)聽窗口關(guān)閉事件,當(dāng)窗口關(guān)閉時,主動去關(guān)閉websocket連接,防止連接還沒斷開就關(guān)閉窗口,server端會拋異常。
window.onbeforeunload = function () {
    websocket.close();
};
}

5、驗證效果

打開谷歌瀏覽器,賬號密碼登錄

打開火狐瀏覽器,模擬不同設(shè)備,賬號密碼登錄

再看下谷歌瀏覽器,頁面彈窗提示

image.png

點擊確定會跳轉(zhuǎn)到登錄頁,谷歌瀏覽器的賬號已經(jīng)被擠下線退出應(yīng)用

谷歌瀏覽器再次登錄,看下火狐瀏覽器,也彈窗提示,之前登錄的賬號也被擠下線退出應(yīng)用

image.png

您的一鍵三連,是我更新的最大動力,謝謝

山水有相逢,來日皆可期,謝謝閱讀,我們再會

我手中的金箍棒,上能通天,下能探海

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容