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分散式鎖基本完成!
加油!!!