我用PHP實作一個帳號只能同時在同一個裝置登錄,注意,不是同一個IP。
之前是在MYSQL的表中加了個顯示是否登入了的字段,若登入了設定為1,退出設定為0.
但後來發現,強行關閉瀏覽器的時候就沒辦法把這個字段設為0了!
想了很久沒想出解決方案,後來在網路上看到好像可以用redis來實現,於是這兩天開始學redis。但發現這樣學下去也沒有什麼思路啊。
所以上來請教一下,請問有誰有經驗的可以說一下怎麼實現嗎?謝謝!祝大家中秋節快樂!
我用PHP實作一個帳號只能同時在同一個裝置登錄,注意,不是同一個IP。
之前是在MYSQL的表中加了個顯示是否登入了的字段,若登入了設定為1,退出設定為0.
但後來發現,強行關閉瀏覽器的時候就沒辦法把這個字段設為0了!
想了很久沒想出解決方案,後來在網路上看到好像可以用redis來實現,於是這兩天開始學redis。但發現這樣學下去也沒有什麼思路啊。
所以上來請教一下,請問有誰有經驗的可以說一下怎麼實現嗎?謝謝!祝大家中秋節快樂!
如果是 Redis 的話, 可以使用 hash 結構來儲存帳戶登錄資訊.
hash 的結構: key field value
hash 相關使用指令 http://redisdoc.com/hash/inde...
具體實現:
hash 結構中, 使用相同的 key field 寫入資料時, 會覆蓋掉歷史資料
<code class="bash"> Redis> hset key field Test Redis> hget key field "Test" Redis> hset key field Run Redis> hget key field "Run" </code>
這樣就能實現單一帳戶的需求, 指定一個key 用來儲存帳戶登錄資訊, field 就是每個帳戶的主鍵, 那麼每次登入都會將上一次的登錄資訊清空, 之前的登錄資訊就失效了, 這樣就能達到之前的登入狀態失效.
如果考慮到不同設備的登入, 可以將 field 變為 devicename-uid 這種形式, 保證一個設備只能夠有一個登入資訊存在.
你要知道你需要什麼?
單一登入還是限制單一設備
單設備是同一台電腦多個瀏覽器?
關於使用mysql的一種解決方法
如果不考慮效率,只需要在mysql中你原有的記錄是否已登錄的字段旁再增加過期時間和設備唯一標識符兩個字段,將以前的判斷是否登錄的條件由「是否為1」變為「是否為1且未過期且設備唯一識別碼一致」。每次使用者有操作時都更新過期時間的值,如果一段時間沒有操作,登入狀態就可以「自動」過期,這樣就可以解決你的「強行關閉瀏覽器的時候就沒辦法把這個欄位設置為0了」的問題。
使用phpredis進行簡單實作
如果你剛接觸redis
,且僅僅需要用redis
做使用者登入的控制,對於資料結構,你不是很了解,string
類型即可滿足你(如果可以,使用hash
可能會更好)。
下面以phpredis擴充提供的相關類別作為背景,進行說明:
假設某一用戶id
為100的帳戶登錄,向redis記錄登入設備資訊
<code class="php"><?php /** * 注册用户登录设备信息 * * 登录后向redis中写入登录的设备标识信息,如果在此之前已经登录了别的设备,之前登录的设备将被强制下线 */ function registerUserDevice() { $userId = 100; // 假设用户id为100 $redis = new Redis(); $redisHost = '127.0.0.1'; $redisPort = 6379; $redis->connect($redisHost, $redisPort); $cacheName = 'deviceUUID:user'.$userId; $deviceUUID = getDeviceUUID(); // 假设有 getDeviceUUID() 函数用于获取/生成设备的唯一标识符 $timeout = 600; // 用户10十分钟无操作自动下线 $redis->set($cacheName, $deviceUUID); $redis->setTimeout($cacheName, $timeout); }</code>
設備每次執行其它操作前,都需要更新redis中設備資訊的過期時間
<code class="php"><?php /** * 延长redis中设备标识信息的生存时间 * * 重新设置redis中用户设备标识信息的过期时间 * @return bool true = 更新成功, false = 更新失败,当前设备需要重新登录 */ function extendDeviceInfoTTL() { $userId = 100; // 假设用户id为100 $redis = new Redis(); $redisHost = '127.0.0.1'; $redisPort = 6379; $redis->connect($redisHost, $redisPort); $cacheName = 'deviceUUID:user'.$userId; $deviceUUID = getDeviceUUID(); // 假设有 getDeviceUUID() 函数用于获取/生成设备的唯一标识符 $timeout = 600; // 用户10十分钟无操作自动下线 $cachedDeviceUUID = $redis->get($cacheName); $isTimeout = false === $cachedDeviceUUID; $isTheRightDevice = $deviceUUID === $cachedDeviceUUID; if($isTimeout || !$isTheRightDevice){ return false; } $redis->setTimeout($cacheName, $timeout); return true; }</code>
裝置中使用者帳號退出時,需要清理redis中的該裝置資訊
<code class="php"><?php /** * 销毁用户设备信息 * * 用在执行登出操作时 */ function delUserDevice() { $userId = 100; // 假设用户id为100 $redis = new Redis(); $redisHost = '127.0.0.1'; $redisPort = 6379; $redis->connect($redisHost, $redisPort); $cacheName = 'deviceUUID:user'.$userId; $redis->delete($cacheName); }</code>
當然了,上面的使用string類型而不是散列類型來實現的解決方案在資源利用和效率上是不太合理的。如果你希望對redis有更深的了解與運用推薦你閱讀《Redis IN ACTION》這本書。具體到php使用redis,你可以選擇使用phpredis擴充或predis。
前段時間做的一個項目大概也有這麼一個東西,大概目的是只能有一個終端在登入這個帳號,也就是不能一個帳號多處同時登入。
解決辦法是在資料庫中新增了一個欄位token,每次登入根據時間戳加其他的生成一個新的token,在整個過程中不斷檢測token,如果發生改變了,那說明有用戶在別處登入。
資料庫加個欄位:臨時的token;等登入後,這個臨時的token會隨機生成,同時使用者會根據這個token生成對應的sesssion;當另外一個裝置登入後,臨時的token更新了;原有裝置的session無法匹配資料庫的token;就會自動跳出!