我是怎麼做App token認證的

使用Token來做身份認證在目前的移動客戶端上非常流行,Token這個概念來源於OAuth認證,主要是在服務端實現。關於相關的原理,同學們自行百度。在這裏,我簡單介紹一下我是怎麼具體實現的,重點描述token生成、token識別及token緩存。

生成Token

服務端接收客戶端傳遞的username和password等請求,在數據庫中檢查,如果用戶名密碼匹配的話,表示登錄成功,服務端生成並返回一個token訪問令牌。

    public function login()
    {
        $data = array_merge($this->request->post(), []);

        // login with password
        $user = Db::name($this->table)->field($this->field_except, true)->where('name', $data['name'])->find();
        if ($user) {
            if ($user['pwd'] === EncryptService::password($data['pwd'])) {
                $this->onLoginSuccess($user);
            } else {
                parent::logic_failure('密碼不正確', 'user_wrong_pwd');
            }
        } else {
            parent::logic_failure('用戶不存在', 'user_not_exists');
        }
    }

    private function onLoginSuccess($user)
    {
        unset($user['pwd']);
        $token = Token::generateToken($this->request, $user);
        $ret = [
            'token' => $token['token'],
            'user' => $user
        ];
        $this->logic_success(null, $ret);
    }

在上面的代碼中,登錄成功,服務端向客戶端返回token和user信息。token是通過Token類的靜態方法generateToken來生成的。

    public static function generateToken($request, $user)
    {
        // 1,生成包含用戶及設備信息的新token
        $data = [
            'uuid' => $request->param('uuid', ''),
            'uid' => $user['id'],
            'ip' => $request->ip(),
            'client' => $request->param('client', ''),
            'update_time' => ApiBase::_timestamp(),
        ];
        // json序列化
        $json = json_encode($data);
        // 加密token信息
        $key = md5(EncryptService::encrypt($json));
        $data['token'] = $key;
        // 2,從數據庫查詢用戶的token,決定是update還是insert
        $token = Token::get($user['id']);
        // 如果數據庫中已存在token,更新
        if ($token) {
            // 刪除舊的緩存
            cache($token['token'], null);
            // 執行更新
            $token->isUpdate(true)->save($data);
        }
        // 不存在,插入
        else {
            $token = new Token();
            $data['create_time'] = ApiBase::_timestamp();
            $token->data($data)->save();
        }
        // 3,將token寫入緩存
        Token::cacheToken($token->getData());
        return $token->getData();
    }

這裏主要有3個步驟

  1. 根據用戶及設備等信息生成一個唯一的token,uid用於識別用戶,uuid用於識別設備,time用於保證唯一。將這些信息序列化後加密並生成md5
  2. 根據用戶id查詢用戶登錄表,不管用戶原來是否已經存在token,新的token將替換舊的token
  3. 將token寫入緩存,因爲token是每個請求都會解析,如果不使用緩存的話,會導致數據庫訪問瓶頸。

客戶端應該保存token,然後在訪問一些需要身份認證的API,比如修改暱稱,就需要帶上這個token了,而不需要顯示帶上用戶信息,比如user_id。

解析token

服務端接收客戶端傳遞的token,需要從中解析出相關的用戶及設備信息。

    public static function getToken($key)
    {
        if (!isset($key)) {
            return null;
        }
        $token = cache($key);
        if ($token) {
            return $token;
        }
        else {
            return Token::findByToken($key);
        }
    }

過程很簡單,先從緩存中取,如果不存在再從數據庫中查詢。所有的請求,服務端都會執行解析token。

驗證token

服務端簡單地把API分爲三類

  1. 公共API,客戶端可以任意訪問,不需要驗證身份,此類API多以GET請求爲多。比如查詢產品列表
  2. 受保護的API,也即需要強制認證的API,這類API需要驗證客戶端身份才能訪問,比如修改頭像,客戶端未傳token或token無效,服務端返回一個錯誤碼。
  3. 可選認證的API,介於1和2之間,不要求強制驗證客戶端身份,比如分享獎勵,如果客戶端傳遞了token並認證通過了,則給相應的獎勵,否則不做任何的獎勵。

基於上面的分析,驗證token附帶一個是否強制驗證的參數,用於中斷。驗證的規則也相對簡單,只要用戶請求的設備ID與token中的設備ID相同就算驗證通過了。

    public function checkToken($exit = true)
    {
        if ($this->token) {
            if ($this->token['uuid'] === $this->request->param('uuid', '')) {
                return true;
            } else {
                if ($exit) {
                    $this->logic_failure(null, 'user_offline');
                } else {
                    $this->token = null;
                }
                return false;
            }
        } else {
            if ($exit) {
                $this->logic_failure(null, 'token_invalid');
            }
            return false;
        }
    }

緩存

緩存相對簡單,緩存的key爲加密token之後的md5值,也即登錄成功後,服務端向客戶端發送的token值。緩存未設置有效期,默認永不過期。

    public static function cacheToken($token)
    {
        if ($token) {
            if (is_object($token)) {
                $token = $token->getData();
            }
            cache($token['token'], ($token));
        }
    }
?>

進階

爲了讓您的應該更加安全,還可以在以下方面強化

  1. token有效期
  2. token緩存加密
  3. token高級校驗

不過,我這裏已經對token做了對稱加密,相信他人僞造token的可能性也不大。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章