1. 程式人生 > >redis實現SSO單點登入,叢集,分散式鎖

redis實現SSO單點登入,叢集,分散式鎖

    使用redis實現單點登入,一般情況下都是配合cookie的.將生成的唯一的token資訊儲存在cookie中,當發生多服務呼叫時,都會在cookie中讀取到該值,再去redis中查詢是否有該使用者資訊存在.如此實現單點登陸

    一般在微服務架構中,SSO單點登陸會抽離出來作為一個單獨的服務,來實現伺服器叢集下的 session共享問題.

    為什麼要使用redis 來解決session 共享問題呢?

    1. redis 是一個純鍵值型別的NSQL資料庫,所有的操作都是具有原子性的.

    2. redis 可以設定key的生存時間,訪問速度快速效率高.

    缺點就是: 會對程式碼有一定的侵入性.需要自行編碼實現!!!

    下面對原有的登陸與單點登入做一下簡單的比較:

    簡單的單服務的登入邏輯:

    

眾所周知,session屬於會話,如果在將來業務需要進行壯大需要改造成多服務,那麼上去解決方案完全行不通!

所以才會引進SSO單點登入的邏輯,它完美的解決了在多服務下的session共享問題,

    :但是又會帶來哪些問題呢?

    1.在高併發下,很難保證生成的token的唯一性,並且有可能存在cookie儲存的token資訊,在redis 中查詢不到,

  於是在這種情況下就需要使用分散式鎖!分散式鎖有很多實現方式,這裡說到的則是利用redis的原子性來實現分散式鎖.

    利用redis 中的setnx 和getset命令來實現redis 分散式鎖!

setnx:

    

getset:

    

    在redis中null 標記為nil;

那麼何為分散式鎖呢?

    字面意思:就是在分散式系統中的鎖!

    那麼這把在分散式中的鎖時怎麼鎖,來確保token的一致性的呢?

首先定義一個鎖的名字(根據登入使用者)作為key("比喻說:XXX_redis_lock"),那麼value採取的時當前時間+超時時間作為value

    /**
     * 加鎖
     *
     * @param key
     * @param time 當前時間 + 超時時間
     * @return
     */
    public boolean lock(String key, Long time) {
        Boolean flag = false;
        // 將值儲存到redis中
        if (RedisPoolUtil.setnx(key, time)) {
            flag = true;
        }
        // 如果該值存在,判斷設定的時間是否超時,如果超時,也將釋放鎖
        Long redisTime = Long.valueOf(RedisPoolUtil.get(key));
        if (!StringUtils.isEmpty(String.valueOf(redisTime)) && redisTime < System.currentTimeMillis()) {
            // shi用redis 的getset api 將新的值儲存到對應的key中 並且返回舊的值
            String oldValue = RedisPoolUtil.getset(key, String.valueOf(time));
            if (!StringUtils.isEmpty(oldValue) && oldValue.equals(String.valueOf(redisTime))) {
                // 說明該鎖已經超時,需要給當前執行緒開啟鎖
                flag = true;
            }
        }
        return flag;
    }

    當鍵值不存在時,第一次訪問(這裡的RedisPoolUtil 是我自己封裝的一個基於JedisPool 的工具類),setnx會設定成功,此時返回的資料為1;再將定義的訊號量修改為true;

    不管成功與否,都需要進行下一步的判斷,防止返回的結果一直為false(有可能某個服務在獲取鎖的過程中出現了bug,但是此時並沒有釋放鎖,如果不做如下的判斷,那麼該判斷將會一直保持false狀態,其他的執行緒將不會得到該鎖物件)

    下面的判斷為,判斷該鎖是否已經超時,如果超時,將直接釋放掉鎖!

    在操作完一些的操作後釋放掉鎖!(將token儲存在cookie,將使用者資訊儲存到redis....)

  /**
     * 釋放鎖
     *
     * @param key
     * @param time
     */
    public void unlock(String key, Long time) {
        // 判斷獲取到的鎖 是否與該鎖相同
        try {
            String currentValue = RedisPoolUtil.get(key);
            if (!StringUtils.isEmpty(currentValue) && currentValue.equals(String.valueOf(time))) {
                RedisPoolUtil.del(key);
            }
        } catch (Exception e) {
            log.error("[釋放鎖] 釋放鎖失敗", e);
        }
    }

至此:redis分散式鎖基本完成!

    加油!!!