1、使用場景
某些業務場景需限定一個賬號只能同時在一個設備登錄。
防異處登錄多用於 APP 中,需配合 API 接口使用。
基於 API 接口實現,在實際線上生產環境中,API 接口是需要做驗證的,即不是隨便被請求就可以拿到數據的。需要對包括但不限於對用戶登錄成功返回的標識、token等的驗證。
2、實現思路
- 用戶註冊完成後每次登錄時,隨機生成一個隨機碼
- 再把用戶賬號+密碼+隨機碼按照一定順序和一定的加密方式加密成一個字符串,此字符串用作 token
- 將隨機碼和 token 儲存到表中
- 登錄成功,返回客戶端用戶賬號和 token 值,供隨後調用其他接口使用
- 用戶每次請求接口時在初始化公共方法裏面做驗證,請求接口需要的公共參數字段有用戶賬號,和登錄所返回的 token,此時用用戶賬號作爲查詢條件取出該用戶的加密隨機字符串和當時生成的 token 值,按照加密時的規則再加密一次獲得 token 和客戶端傳過來的進行比對,錯誤則駁回此次請求並返回空數據,並提示賬號可能存在風險或已在其他設備登錄。
- 用戶賬號可以替換爲用戶的唯一id,更簡短,保證唯一即可
3、效果描述
當用戶按照流程註冊登錄後,生成第一次的加密隨機字符串和 token ,返回給客戶端正常通過驗證獲取其他接口數據,當此賬號在另一個設備登錄時,此賬號的隨機字符串和 token 被更新,在當前設備請求接口時,token 比對不正確,提示退出登錄。
4、部分代碼(thinkphp5)
用戶登錄
$account = $params['account'];
$password = $params['password'];
// 省略判斷賬號是否存在和密碼是否正確
// 生成隨機字符串
// createRandStr()需要自己封裝
$salt = createRandStr();
// 加密成 token
$token = md5($account.$pwd.$salt);
// 更新到數據庫
$update = Db::name('member')
->where('account', $account)
->update(['salt' => $salt, 'token' => $token]);
// 返回到客戶端
// result()方法需自己封裝
$this->result('登錄成功', ['account' => $account, 'token' => $token], 200);
接口初始化公共方法
public function _initialize()
{
parent::_initialize();
$params = $this->request->post();
$account = $params['account'];
$token = $params['tokens'];
if (!$account || !$token) {
$this->result('參數錯誤', [], 100);
}
// token驗證放在了model中
$model = new Check();
if (!$model->check($account, $token)) {
$this->result('token錯誤', [], 101);
}
}
模型 token 驗證
public function check($account, $token)
{
$member = $this->where('account', $account)->find();
if (md5($account.$member['password'].$member['salt']) != $token) {
return false;
} else {
return true;
}
}