移動端後臺開發 (加密驗證--非通用介面篇)

JRzhang發表於2017-03-29

前言

上次說完了通用介面加密驗證,這次來說一下非通用介面的驗證,在說之前我們來看一下移動端需要通過非通用介面時的表欄位。

一.表欄位

id 主鍵ID
uid 使用者ID
deviceid 裝置唯一識別碼
token 加密使用欄位(可以使用UUID1)
token_time token過期時間

二、非通用介面

回憶下這段程式碼,來看看非通用方法裡做了什麼

private function verify($request)
{

    if($request->path()=='login'){
       // 通用方法
        return  self::$verify->common($request);
    }else{
       // 非通用方法 
        return self::$verify->proprietary($request);
    }
    return false;
}

proprietary裡都做了一下操作

public function proprietary($request)
{
    if($request->all()){
        $data = $request->all();
        // 時間驗證
        $ckTime = $this->checkTime($data['time']);
        if(!$ckTime) return 'SN002';
        // 因為是非通用介面,這裡的id,就需要是使用者表裡的ID,
        if(!isset($data['id'])) return "SN004";
        // 根據版本設計不同的驗證
        switch ($data['version']){
            case $data['version'] >'1.4' && $data['version']<'2.0':
                $temp = $this->checkProprietary_v1($request);
                break;
            default:
                $temp = $this->checkProprietary_v2($request);
                break;
        }
        if($temp){
            switch ($temp){
                case 'SN007':   // 使用者不存在
                    return 'SN007';
                    break;
                case 'SN008':  // 其他裝置登入
                    return 'SN008';
                    break;
                case 'SN009':  // token 過期
                    return 'SN009';
                    break;    
                default:      // 驗證成功
                    return 'SN200';
                    break;
            }
        }
            return "SN005";
    }
    return false;
}

在上邊操作中,把請求傳送到了checkProprietary_v1方法中,在看這個方法之前先看一下user方法

//這裡根據key從快取中讀取資料,如果不存在就去資料庫中獲取資料,然後存在快取中
public function user($id)
{
    $key='USER:MESSAGE:'.$id;
    if(!\Redis::exists($key)){
        $userJson = Common::curl('/getToken',['id'=>$id],1);
        $user = json_decode($userJson,1);
        if($user['ServerNo'] == 'SN200'){
            \Redis::hMset($key,$user['ResultData']);
            return $user['ResultData'];
        }else{
            return false;
        }
    }
    return \Redis::hGetall($key);
}

現在再看checkProprietary_v1方法

private function checkProprietary_v1($request)
{

    $data = $request->all();
    $path = $request->path();
    $param = $data['param'];
    $id = $data['id'];
    $signature = $data['signatures'];
    $time = $data['time'];
    $deviceid = $data['deviceid'];

    $user = $this->user($id);
    if(!$user) return 'SN007';  // 使用者不存在
    $tokentime = $user['token_time']; //獲取token的過期時間
    if($deviceid != $user['deviceid']){
       return 'SN008';  // 識別碼不同 拋其他裝置登入 並重新登入
    }
    if($time > $tokentime){
        return 'SN009';     // token超時 重新登入
    }
    $token = $user['token']; // 獲取使用者的token
    //需注意生成的token為32位的字串
    $hashs = [
        [1,2,23,28,23,45],
        [6,8,19,25,30,31],
        [0,25,31,3,4,8],
        [2,31,0,9,3,17],
        [29,2,1,17,21,26],
        [10,5,18,9,2,3],
        [5,10,15,17,18,22],
        [8,20,22,37,19,21],
        // 可以繼續定義陣列
    ];
    $strs =substr($token,4,1);
    $strs.=substr($token,5,1);
    $strs.=substr($token,9,1);
    $code = hexdec($strs);
    $str1 = $code%8;
    $arr =$hashs["$str1"];
    $m = null;
    foreach($arr as $v){
        $m.= substr($token,$v,1);
    }
    $str = md5($path.$time.$id.$param.$m);
    if($signature == $str){
        return 'SN200';
    }else{
        return false;
    }
}

解釋:

使用者每次登陸時,賬號密碼驗證通過將以下欄位存入資料庫,如UID存在則進行修改

uid // 使用者ID 唯一
deviceid // 裝置唯一識別碼,每次登陸時都需獲取一次
token // 每次登陸都需要重新生成一條
token_time // 需改變 當前時間+希望保持的時間

注意點:

1.登陸成功後,一定要報token返回給移動端
2.在移動端也要儲存$hashs陣列,並使用一樣的演算法

最後貼出一段根據狀態碼返回資訊提示的程式碼

 public function handle($request, Closure $next)
{
    $time = time();
    switch($this->verify($request))
    {
        case "SN200":
            $temp =  $next($request);
            // 封裝
            return $temp;
            break;
        case "SN001":
            return response()->json(['serverTime'=>$time,'ServerNo'=>'SN001','ResultData'=>'Server internal error!']);
            break;
        case "SN002":
            return response()->json(['serverTime'=>$time,'ServerNo'=>'SN002','ResultData'=>'Request timeout!']);
            break;
        case "SN003":
            return response()->json(['serverTime'=>$time,'ServerNo'=>'SN003','ResultData'=>'Version number exception!']);
            break;
        case "SN004":
            return response()->json(['serverTime'=>$time,'ServerNo'=>'SN004','ResultData'=>'Global user ID can not be null!']);
            break;
        case "SN005":
            return response()->json(['serverTime'=>$time,'ServerNo'=>'SN005','ResultData'=>'Signature error!']);
            break;
        case "SN007":
            return response()->json(['serverTime'=>$time,'ServerNo'=>'SN007','ResultData'=>'user not!']);
            break;
        case "SN008":
            return response()->json(['serverTime'=>$time,'ServerNo'=>'SN008','ResultData'=>'Other devices login!']);
            break;
        case "SN009":
            return response()->json(['serverTime'=>$time,'ServerNo'=>'SN009','ResultData'=>'token time out!']);
            break;
        default:
            return response()->json(['serverTime'=>$time,'ServerNo'=>'SN006','ResultData'=>'No access!']);
    }

}

相關文章