tp5 接口

一,爲api項目搭建數據庫


設計數據庫的小技巧

  1. 一個對象,一張表
  2. 一張表,一個主鍵
  3. 表名用 數據庫名 作前綴
  4. 字段名用 表名 作前綴
  5. 前綴後面加 縮寫 (容易看懂的前提下)
  6. 數據表的關係處理

二,爲api項目寫接口文檔


用戶登陸舉例javascript

# 判斷數據庫中是否有此用戶
post www.test.com/api api.test.com
參數 必選 類型 說明
time true int 時間戳 (用於肯定接口的訪問時間)
token true string 肯定訪問者身份 (MD5(USER_MD5(time)_USER))
username true string 只接受郵箱
password true string 用戶密碼
{
    "ret": 200, // 返回結果狀態。200:接口正常請求並返回/40*:服務端的數據有誤/500:服務器運行錯誤
    "data": {
        "user_id": "27", // 用戶id
        "user_tag": "1" // 用戶身份
    },
    "msg": "" // 401:用戶名不存在!/402:手機號不存在!/403:密碼不正確!
}

三,配置URL


需求分析

· api.test.com ===> www.test.com/index.php/apiphp

爲api配置二級域名
  1. 打開 httpd-vhosts.conf 添加 ServerAlias api.test.com
  2. 配置 host 文件 127.0.0.1 api.test.com
使用tp5路由進行URL解析
  • 新建User.php文件,路徑 G:tp5/application/api/controller/User.php
<?php
namespace app\api\controller;
class User{
    public function index($id){
        echo '<br/>';
        echo $id;
    }
}
  • 修改 config.php (開啓路由)
// 是否開啓路由
'url_route_on' => true,
// 域名部署
'url_domain_deploy' => true,
  • 配置路由規則 route.php
<?php
use think\Route;

// api.test.com ===> www.test.com/index.php/api
Route::domain('api','api');

四,接口安全


常見的安全問題

  1. 接口被大規模調用消耗系統資源,影響系統的正常訪問,甚至系統癱瘓
解決方案: 獲取 timestamp (時間戳), 設置接口失效時間
  1. 接口數據被黑客篡改(僞造請求)
解決方案: 對參數加密, 生成 token , 判斷 token 是否正確
  1. 數據被黑客截取
解決方案: 使用 https , 用證書對數據進行加密, 即便數據被截取, 對黑客也沒有意義
黑客能夠獲取數據, 可是沒法獲取數據的加密方法
api項目的安全設計
  1. time
時間戳, 用於判斷請求是否超時, 設置爲30秒
  1. token
其餘參數加密而來, 保證數據不被篡改
  1. 敏感信息加密傳輸
接收加密過的用戶密碼, 用戶密碼永不返回
最好使用 https, 全部信息都會被加密

五,開發前準備工做(參數過濾)

使用 common.php 統一處理參數過濾 G:tp5/application/api/controller/Common.phpjava

<?php
namespace app\api\controller;
use think\Controller;
use think\Request;
use think\Validate;
class Common extends Controller {
    protected $request; // 用來處理參數
    protected $validater; // 用來驗證數據/參數
    protected $params; // 過濾後符合要求的參數
    protected $rules = array(
            'User'=>array(......);
    protected function _initialize() {
        parent::_initialize();
        $this->request = Request::instance();
        $this->check_time($this->request->only(['time']));
        $this->check_token($this->request->param());
        $this->params=$this->check_params($this->request->except(['time','token']));
    }

自定義返回信息函數 G:tp5/application/api/controller/Common.php數據庫

/**
* api 數據返回
 * @param  [int] $code [結果碼 200:正常/4**數據問題/5**服務器問題]
 * @param  [string] $msg  [接口要返回的提示信息]
 * @param  [array]  $data [接口要返回的數據]
 * @return [string]       [最終的json數據]
*/
public function return_msg($code, $msg = '', $data = []) {
    /*********** 組合數據  ***********/
    $return_data['code'] = $code;
    $return_data['msg']  = $msg;
    $return_data['data'] = $data;
    /*********** 返回信息並終止腳本  ***********/
    echo json_encode($return_data);die;
}

驗證 time G:tp5/application/api/controller/Common.phpjson

/**
* 驗證請求是否超時
 * @param  [array] $arr [包含時間戳的參數數組]
 * @return [json]      [檢測結果]
*/
public function check_time($arr) {
    if (!isset($arr['time']) || intval($arr['time']) <= 1) {
        $this->return_msg(400, '時間戳不正確!');
    }
    if (time() - intval($arr['time']) > 60) {
        $this->return_msg(400, '請求超時!');
    }
}

驗證token G:tp5/application/api/controller/Common.phpapi

/**
* 驗證token(防止篡改數據)
 * @param  [array] $arr [所有請求參數]
 * @return [json]      [token驗證結果]
*/
public function check_token($arr) {
    /*********** api傳過來的token  ***********/
    if (!isset($arr['token']) || empty($arr['token'])) {
        $this->return_msg(400, 'token不能爲空!');
    }
    $app_token = $arr['token']; // api傳過來的token
    /*********** 服務器端生成token  ***********/
    unset($arr['token']);
    $service_token = '';
    foreach ($arr as $key => $value) {
        $service_token .= md5($value);
    }
    $service_token = md5('api_' . $service_token . '_api'); // 服務器端即時生成的token
    /*********** 對比token,返回結果  ***********/
    if ($app_token !== $service_token) {
        $this->return_msg(400, 'token值不正確!');
    }
}

爲每一個接口配置驗證規則 G:tp5/application/api/controller/Common.php數組

protected $rules = array(
    'User' => array(
        'login' => array(
            'user_name' => ['require', 'chsDash', 'max' => 20],
            'user_pwd'  => 'require|length:32',
        ),
    ),
);

驗證參數 G:tp5/application/api/controller/Common.php安全

/**
* 驗證參數 參數過濾
 * @param  [array] $arr [除time和token外的全部參數]
 * @return [return]      [合格的參數數組]
*/
public function check_params($arr) {
    /*********** 獲取參數的驗證規則  ***********/
    $rule = $this->rules[$this->request->controller()][$this->request->action()];
    /*********** 驗證參數並返回錯誤  ***********/
    $this->validater = new Validate($rule);
    if (!$this->validater->check($arr)) {
        $this->return_msg(400, $this->validater->getError());
    }
    /*********** 若是正常,經過驗證  ***********/
    return $arr;
}

五,獲取驗證碼


驗證碼原理服務器

  • 生成及發送
點擊獲取驗證碼
發送郵箱號到後臺
後臺生成郵箱驗證碼
用session保存驗證碼及郵箱
發送郵件
  • 驗證
獲取用戶輸入的驗證碼及郵箱
取出session保存的內容
對比驗證
返回信息, 結束

配置路由 G:tp5/application/route.phpsession

注意: get方式沒有參數名, 因此要注意參數的順序, 對號入座.
// 獲取驗證碼
Route::get('code/:time/:token/:username/:is_exist','code/get_code');

參數過濾 G:tp5/application/api/controller/Common.php

在common.php裏簡單過濾, 具體驗證放在code.php裏
'Code' => array(
    'get_code' => array(
        'username' => 'require',
        'is_exist' => 'require|number|length:1',
    ),
),

檢測用戶名 G:tp5/application/api/controller/Code.php

namespace app\api\controller;
use phpmailer\phpmailer;
use submail\messagexsend;
class Code extends Common {
    public function get_code() {
        $username      = $this->params['username'];
        $exist         = $this->params['is_exist'];
        $username_type = $this->check_username($username); // 檢查用戶名, 決定用下面哪那個函數
        switch ($username_type) {
        case 'phone':
            $this->get_code_by_username($username, 'phone', $exist); // 經過手機獲取驗證碼
            break;
        case 'email':
            $this->get_code_by_username($username, 'email', $exist); // 經過郵箱獲取驗證碼
            break;
        }
    }
}

Common.php中寫一個判斷用戶名類型的函數

public function check_username($username) {
    /*********** 判斷是否爲郵箱  ***********/
    $is_email = Validate::is($username, 'email') ? 1 : 0;
    /*********** 判斷是否爲手機  ***********/
    $is_phone = preg_match('/^1[34578]\d{9}$/', $username) ? 4 : 2;
    /*********** 最終結果  ***********/
    $flag = $is_email + $is_phone;
    switch ($flag) {
    /*********** not phone not email  ***********/
    case 2:
        $this->return_msg(400, '郵箱或手機號不正確!');
        break;
    /*********** is email not phone  ***********/
    case 3:
        return 'email';
        break;
    /*********** is phone not email  ***********/
    case 4:
        return 'phone';
        break;
    }
}

經過用戶名(手機/郵箱)獲取驗證碼 G:tp5/application/api/controller/Code.php

public function get_code_by_username($username, $type, $exist) {
    if ($type == 'phone') {
        $type_name = '手機';
    } else {
        $type_name = '郵箱';
    }
    /*********** 檢測手機號/郵箱是否存在  ***********/
    $this->check_exist($username, $type, $exist);
    /*********** 檢查驗證碼請求頻率 30秒一次  ***********/
    if (session("?" . $username . '_last_send_time')) {
        if (time() - session($username . '_last_send_time') < 30) {
            $this->return_msg(400, $type_name . '驗證碼,每30秒只能發送一次!');
        }
    }
    /*********** 生成驗證碼  ***********/
    $code = $this->make_code(6);
    /*********** 使用session存儲驗證碼, 方便比對, md5加密   ***********/
    $md5_code = md5($username . '_' . md5($code));
    session($username . '_code', $md5_code);
    /*********** 使用session存儲驗證碼的發送時間  ***********/
    session($username . '_last_send_time', time());
    /*********** 發送驗證碼  ***********/
    if ($type == 'phone') {
        $this->send_code_to_phone($username, $code);
    } else {
        $this->send_code_to_email($username, $code);
    }
}

判斷用戶名(手機/郵箱)在數據庫中是否應該存在(由於要分兩種狀況1,註冊 2,修改)
當咱們註冊的時候數據庫裏不能有,當咱們修改的時候數據庫裏必須有
G:tp5/application/api/controller/Common.php

public function check_exist($value, $type, $exist) {
    $type_num  = $type == "phone" ? 2 : 4;
    $flag      = $type_num + $exist;
    $phone_res = db('user')->where('user_phone', $value)->find();
    $email_res = db('user')->where('user_email', $value)->find();
    switch ($flag) {
    /*********** 2+0 phone need no exist  ***********/
    case 2:
        if ($phone_res) {
            $this->return_msg(400, '此手機號已被佔用!');
        }
        break;
    /*********** 2+1 phone need exist  ***********/
    case 3:
        if (!$phone_res) {
            $this->return_msg(400, '此手機號不存在!');
        }
        break;
    /*********** 4+0 email need no exist  ***********/
    case 4:
        if ($email_res) {
            $this->return_msg(400, '此郵箱已被佔用!');
        }
        break;
    /*********** 4+1 email need  exist  ***********/
    case 5:
        if (!$email_res) {
            $this->return_msg(400, '此郵箱不存在!');
        }
        break;
    }
}

Code.php 中生成驗證碼

public function make_code($num) {
    $max = pow(10, $num) - 1;
    $min = pow(10, $num - 1);
    return rand($min, $max);
}

只介紹郵箱發送驗證碼

開啓郵箱smtp
php 開啓php_openssl
G:tp5/application/api/controller/Code.php
public function send_code_to_email($email, $code) {
    $toemail = $email;
    $mail    = new PHPMailer();
    $mail->isSMTP();
    $mail->CharSet    = 'utf8'; // 設置字符集
    $mail->Host       = 'smtp.qq.com'; // smtp服務器
    $mail->SMTPAuth   = true;
    $mail->Username   = "123456789@qq.com";
    $mail->Password   = "asd1151sad51dsa"; // 本身設置的smtp密碼, 與登陸密碼無關
    $mail->SMTPSecure = 'ssl';
    $mail->Port       = 465;
    $mail->setFrom('123456789@qq.com', '接口測試');
    $mail->addAddress($toemail, 'test');
    $mail->addReplyTo('123456789@qq.com', 'lee');
    $mail->Subject = "您有新的驗證碼!"; // 郵件標題
    $mail->Body    = "這是一個測試郵件,您的驗證碼是$code,驗證碼的有效期爲1分鐘,本郵件請勿回覆!"; // 郵件內容
    if (!$mail->send()) {
        $this->return_msg(400, $mail->ErrorInfo);
    } else {
        $this->return_msg(200, '驗證碼已經發送成功,請注意查收!');
    }
}
相關文章
相關標籤/搜索