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

JRzhang發表於2017-03-29

前言

在Web開發中瀏覽器與伺服器進行通訊,在伺服器端我們可以用"session"來確定使用者的身份,然後根據從瀏覽器接收到的引數,伺服器返回給瀏覽器相應的資料。但是在移動端與伺服器進行通訊時,並沒有"session"的存在(也許是我不知道),不過我們可以要求移動端提交引數的時候把使用者的"id"或"Guid"和其他引數一起提交過來。然後根據請求的引數來返回對應的內容.

那麼問題來了,我可以使用抓包工具獲取別人提交"url地址"與"引數",然後進行模擬操作,我就可以獲取該使用者的全部資料,如果涉及到金錢的話,更是一場毀滅性的災難。所以,在開始一個移動端的專案前,加密驗證是最先考慮與準備的事情.

畢竟誰也不希望,有人可以在自己家裡面隨意的進進出出!

一、介面分類

在我們日常接觸的app中,像"登入","註冊","找回密碼"這種沒有登入的情況下,都可以訪問的介面,我們稱為"通用介面",像"個人資訊","支付密碼",這種涉及到個人隱私或者金錢的介面,我們稱為"非通用介面",再次重複一下,介面分為

1.通用介面 // 加密程度略低於非通用介面,但必須要做加密
2.非通用介面 // 必須是你知道的最安全的加密方式

附上程式碼

private static $verify;

// Verify 為注入檔案
public function __construct(Verify $verify)
{
    self::$verify = $verify;
}

 private function verify($request)
{

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

public function handle($request, Closure $next)
{
    $time = time();
     // 此處呼叫上邊的verify方法,根據返回的狀態碼,來返回移動端資訊提示
    switch($this->verify($request))
    {
        case "SN200":
            $temp =  $next($request);
            return $temp;
            break;
        //最後在完善其他狀態碼資訊 
    }

}

下面我們看看訪問 login 路由時, 通用方法都做了什麼?

 public function common($request)
{
   // 接收request物件
    if($request->all()){
        $data = $request->all();
        // 時間驗證
        // $data['time']為移動端當前時間戳
        $ckTime = $this->checkTime($data['time']);
        // SN002 請求超時 
        if(!$ckTime) return 'SN002';
        // 驗證ID的存在
        // SN004 使用者沒有登入
        if(!isset($data['id'])) return 'SN004';
            // 根據移動端傳遞的版本號來進行加密驗證
            switch ($data['version']){
                case $data['version'] >'1.4' && $data['version']<'2.0':
                        // 開始進行通用加密驗證
                    $temp = $this->checkCommon_v1($request);
                    break;
                default:
                    $temp = $this->checkCommon_v2($request);
                    break;
            }
            if($temp){
               // SN200 驗證通過
                return "SN200";
            }
            // SN005 簽名錯誤
            return "SN005";
    }
     // 伺服器有請求,但引數為空,通常與安卓和ios的框架有關係(需注意)
    return false;
}

程式碼與註解:

  1. 時間驗證

    public function checkTime($time)
    {
        $Time_difference = abs(time()-$time);
        if($Time_difference>30){
                return false;
        }
        return true;
     }

    請求到達伺服器的時間減去移動端本地的時間戳, 如果大於 30 秒, 就代表請求超時,有可能是該使用者的網路不算太好。如果不使用abs()函式的話,萬一我的資料包被別人抓住了他可以無限大的修改$time這個值,那麼使用伺服器時間戳去相減的到的一直是負數,還是小於 30秒的。由此可見使用abs()函式會更加的安全一些。

  2. 驗證ID的存在

主要是為接下來的非通用介面驗證做準備。通用介面的ID可以與移動端事前定義一個值。"移動端與服務端ID的值一定要相同,要不然沒辦法做接下來的驗證"

  1. 根據移動端傳遞的版本號來進行加密驗證

每個大版本的改變,加密的方式都應隨著改變而改變。小版本的修正就沒有必要去更換加密方式了。

  1. 通用加密驗證

    private function checkCommon_v1($request)
    {
            $data = $request->all();
            $path = $request->path();
            $time = $data['time'];
            $id = '1';
            $param = $data['param'];
            $cryptToken = "JRzhang";
            $signature = md5($path.$time.$id.$param.$cryptToken);
            if($signature!=$data['signatures']){
                    return false;
            }else{
                    return true;
            }
    }

這裡說一下移動端應傳遞的引數

time 移動端當前時間
param 真正的請求引數 陣列轉為json後的字串
version 移動端當前版本
deviceid 裝置唯一識別碼(非通用介面使用,用於單點登入)
signature 簽名移動端應按照上邊的引數順序進行MD5加密
cryptToken 移動端儲存一份與伺服器端相同,但不進行引數傳遞只做加密使用

坑點: 伺服器對 android 進行介面除錯時,一定要注意 android本地的時間戳。因為android手機可以tmd調整本地時間.

最後如果移動端傳遞的signature與伺服器端生成的$signature相同, 那麼恭喜你,你可以從我家的大門進來了, 但只可以在院子裡轉一轉, 如果想進屋裡,請繼續關注

相關文章