ThinkPHP 之 手機端API開發《一》

說在前面的話:Hello,你們好。我是Rojer,從今天開始,我將用公衆號miguphp以及這裏記錄個人開發歷程,與你們分享。僅適用小白食用,大牛請繞道php

好了,廢話很少說。進入今天正題。html

最近我在作一款餐廳排號和預定的APP,名字暫時定作「食在」。APP用的是ionic寫,後臺以及API打算使用thinkphp+bootstrap,畢竟tp是我第一個學的php框架,比較熟悉。O(∩_∩)O~mysql


規劃

API請求的執行流程:git

  • router:路由,把請求路由到controllergithub

  • 權限認證:ajax

    • 登陸驗證sql

    • 行爲權限判斷thinkphp

    • 模型擁有權限判斷數據庫

  • 請求被轉發到控制器執行json

  • 返回數據結果

模型擁有權限判斷 : 好比店長具備寫店鋪信息的權限:即「行爲權限判斷」,可是要判斷他寫入的對象是否是belong to 他,因此有了這個判斷,應該很好理解吧

計劃好上面的流程,因此咱們能夠想一想TP要怎麼作呢?

  • 路由:Tp自己就有路由啦,直接拿來用,很少說了。

  • 請求被轉發到控制器執行:這個也是由TP的路由本身有的,因此也沒啥好多說的,主要仍是控制器中間的邏輯

  • 權限驗證:this is a big question!既然router能夠直接把請求轉到控制器下,那麼咱們要怎麼作權限驗證呢?難道要在每一個控制器裏面寫一遍嗎?固然,其實咱們有其餘的解決方法:你們不知道有沒有看到其實tp也支持apo面向切入的,在tp叫作Behavior

關於ThinkPHP基礎

下載thinkphp:http://www.thinkphp.cn/down.html
thinphp文檔地址:http://www.kancloud.cn/manual/thinkphp/1...

而後把它放到本地服務器下,我用的是MAMP,window系統建議使用xmapp

(如不會請自行百度,xmapp已經算傻瓜操做了,下載就能用,還附帶mysql和phpmyadmin)

訪問http://localhost

就能夠看到Application目錄下面多了Home目錄,之後咱們主要在這下面進行開發。
而後你須要作的是填寫好數據庫的信息,與數據創建鏈接

創建Router

在配置Home/Conf/config.php中開啓路由,同時爲了將路由單獨分離出來,咱們在這個文件夾下面單獨建一個router.php,並在Home/Conf/config.php這樣寫:

<?php
return array(
    //'配置項'=>'配置值'
    'URL_ROUTER_ON'   => true,
    'URL_ROUTE_RULES'=> include_once __DIR__.'/router.php',
);

以後咱們在router.php添加測試路由

<?php
return array(
    'news/:id'               => 'Index/index',
);

訪問http://localhost/news/1,以後咱們發現沒法加載模塊:News的錯誤提示,那是由於咱們沒有綁定默認模塊,因此必須用http://localhost/Home/news/1訪問,或者在index.php添加define('BIND_MODULE','Home');

解決好上面問題,咱們就能看主頁了。

設置權限驗證類

創建數據庫

  • 創建用戶表

CREATE TABLE `users` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `tel` varchar(19) NOT NULL COMMENT '手機號',
      `password` char(32) NOT NULL COMMENT '密碼',
      `avatar` varchar(500) DEFAULT NULL COMMENT '頭像',
      `nick` varchar(45) DEFAULT NULL COMMENT '暱稱',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  • 創建AccessToken表

CREATE TABLE `access_token` (
      `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
      `token` char(60) NOT NULL DEFAULT '',
      `uid` int(11) NOT NULL COMMENT '用戶id',
      `failuretime` int(11) NOT NULL COMMENT '失效時間 時間戳',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  • 創建角色組表

CREATE TABLE `role_group` (
      `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
      `name` varchar(20) DEFAULT NULL COMMENT '用戶組名稱',
      `rules` varchar(1000) DEFAULT NULL COMMENT '規則數組',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  • 創建規則表

CREATE TABLE `rule` (
      `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
      `action` varchar(20) DEFAULT NULL COMMENT '動做標識 惟一標識',
      `name` varchar(30) DEFAULT NULL COMMENT '規則名稱',
      `condition` varchar(300) NOT NULL DEFAULT '' COMMENT '條件',
      `type` int(1) NOT NULL DEFAULT '0' COMMENT '\n      0 - member\n      1 - owner\n      2 -  everyone ',
      PRIMARY KEY (`id`),
      UNIQUE KEY `action` (`action`)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
  • 創建用戶歸屬角色組表

CREATE TABLE `role_user_play` (
      `uid` int(11) NOT NULL,
      `rid` int(11) NOT NULL,
      PRIMARY KEY (`uid`,`rid`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

編寫權限驗證類

創建好以上表,下面咱們來創建權限驗證的類庫擴展
ThinkPHP/Libaray/Org/Util下創建Auth.class.php

代碼以下

<?php
/**
 * Created by PhpStorm.
 * User: rojer
 * Date: 16/3/30
 * Time: 下午1:38
 */

namespace Org\Util;


class Auth
{

    private $user = null;
    private $table_users = 'users';
    private $table_access_token = 'access_token';
    private $table_role_group = 'role_group';
    private $table_rule = 'rule';
    private $table_role_user_play = 'role_user_play';
    private $errormsg = '';

    const TYPE_MEMBER = 0;
    const TYPE_OWNER = 1;
    const TYPE_EVERYONE = 2;

    private $targetModel = null;
    private $targetKey = null;
    private $foreignKey = null;

    public function __construct() {
        $this->foreignKey = $this->table_users.'_id';
    }

    /**
     * 設置檢查模塊對象信息
     * @param $t 對象模塊名稱
     * @param $key 目標主鍵
     * @param null $f 外鍵
     */
    public function setTargetModel($arr){
        $t = $arr[0];
        $key = $arr[1];
        $f = $arr[2];

        $f == null || $this->foreignKey = $f;
        $this->targetModel = $t;
        $this->targetKey = $key;
    }
    /**
     * 判斷用戶是否已經登陸
     *
     * @param $accesstoken
     * @return bool
     */
    private function checkLogin(){
        $userModel = M($this->table_users);
        $accesstoken = I('server.HTTP_ACCESS_TOKEN',null);
        if(!$accesstoken){
            $this->errormsg = '請輸入您的accesstoken!';
            return false;
        }
        $accessTokenModel = M($this->table_access_token);
        $tokenInfo = $accessTokenModel->where("'token = '%s'",$accesstoken)->find();
        if(!$tokenInfo){
            $this->errormsg = '認證失敗!';
            return false;
        }

        if($tokenInfo['failuretime'] < time()){
            $this->errormsg = '認證已經失效,請從新認證!';
            return false;
        }

        $this->user = $userModel->find($tokenInfo['uid']);
        return true;
    }

    public function refreshToken(){
        $accesstoken = I('server.HTTP_ACCESS_TOKEN',null);
        if(!$accesstoken){
            $this->errormsg = '請輸入您的accesstoken!';
            return false;
        }
        $accessTokenModel = M($this->table_access_token);
        $tokenInfo = $accessTokenModel->where("'token = '%s'",$accesstoken)->find();
        if(!$tokenInfo){
            $this->errormsg = '無效請輸入您的accesstoken!';
            return false;
        }

        if($tokenInfo['failuretime'] < time()){
            $this->errormsg = '認證已經失效,請從新認證!';
            return false;
        }

        $time = strtotime("+30 day");

        $data = array(
            'failuretime'=> $time
        );
        $accessTokenModel->where("'token = '%s'",$accesstoken)->save($data);
        return $time;
    }


    /**
     * 判斷用戶是否具備操做權限
     *
     * @param $actionname
     * @return bool
     */
    public function checkPermission($actionname){
        //獲取動做的定義信息
        $rule = $ruleModel = M($this->table_rule)->where("action = '%s'",$actionname)->find();

        //未定義的動做和容許任何人操做的動做直接經過
        if(!$rule || $rule['type'] == $this::TYPE_EVERYONE)
            return true;

        //判斷用戶登陸與非
        if(!$this->checkLogin())
            return false;

        $checkResult = false;
        //獲取該用戶的用戶組並關聯出該用戶組擁有的所有動做id集合
        $roleUserPlayModel = M($this->table_role_user_play)->where('uid = %d',$this->user['id']);
        $roleUserPlayModel = $roleUserPlayModel->join($this->table_role_group.' AS table_role_group on '.$this->table_rule.'.rid = table_role_group.id','LEFT');
        $groups = $roleUserPlayModel->select();
        $permission_ids = array();
        foreach($groups as $group){
            $ids = explode(',',$groups['rules']);
            $permission_ids = array_merge($permission_ids,$ids);
        }
        $permission_ids = array_unique($permission_ids);

        //判斷動做是否
        if(in_array($rule['id'],$permission_ids))
            $checkResult = true;

        if (!empty($rule['condition'])) {
            //條件驗證
            $user = $this->user;
            $command = preg_replace('/\{(\w*?)\}/', '$user[\'\\1\']', $rule['condition']);
            //dump($command);//debug
            $condition = false;
            @(eval('$condition=(' . $command . ');'));

            if (!$condition) {
                $this->errormsg = "用戶 $command ,不符合要求的 ".$rule['condition']." 條件!";
                return false;
            }
        }

        //判斷是否須要驗證用戶是否擁有模塊
        if($rule['type'] == $this::TYPE_OWNER){
            $checkResult = $this->checkOwner();
        }
        return $checkResult;
    }

    /**
     * 判斷操做對象全部權
     *
     * @param $checkmodel
     * @param string $foreign
     * @return bool
     */
    private function checkOwner(){
        if($this->targetModel == null || $this->targetKey == null)
            return false;
        $result = M($this->targetModel)->find($this->targetKey);
        if($result[$this->foreignKey] == $this->user['id']){
            return true;
        }else{
            $this->errormsg = '您沒有權限操做該資源!';
            return false;

        }
    }

    /**
     * 頒發用戶token
     *
     * @param $uid
     * @param int $days
     * @param $time
     * @return null|string
     */
    public function awardAccessToken($uid,$days = 30,&$time){
        $token = $this->getRandChar();
        $accessTokenModel = M($this->table_access_token);
        $time = strtotime("+$days day");
        $data = array(
            'token'=>$token,
            'uid'=>$uid,
            'failuretime'=> $time
        );
        $accessTokenModel->add($data);
        return $token;
    }

    /**
     * 獲取錯誤信息
     * @return string
     */
    public function getErroeMsg(){
        return $this->errormsg;
    }

    /**
     * 生成隨機字符串
     * @param int $length
     * @return null|string
     */
    private function getRandChar($length = 60){
        $str = null;
        $strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
        $max = strlen($strPol)-1;

        for($i=0;$i<$length;$i++){
            $str.=$strPol[mt_rand(0,$max)];//mt_rand($min,$max)生成介於min和max兩個數之間的一個隨機整數
        }
        return $str;
    }
}

設置驗證權限行爲

Application/Common/Conf下創建tags.php內容以下:

<?php
/**
 * Created by PhpStorm.
 * User: rojer
 * Date: 16/3/30
 * Time: 下午1:59
 */

return array(
    'auth'=>array('Home\\Behaviors\\AuthBehavior'),
);

而後咱們在Application/Home/Behaviors下創建AuthBehavior.class.php,代碼以下:

<?php
namespace Home\Behaviors;
/**
 * Created by PhpStorm.
 * User: rojer
 * Date: 16/3/30
 * Time: 下午2:01
 */

class AuthBehavior extends \Think\Behavior
{

    /**
     * 執行行爲 run方法是Behavior惟一的接口
     * @access public
     * @param mixed $params 行爲參數
     * @return void
     */
    public function run(&$params)
    {
        $auth = new \Org\Util\Auth();
        $params->setAuth($auth);
        $auth->setTargetModel($params->getTargetModel());
        $auth_result = $auth->checkPermission(CONTROLLER_NAME.'::'.ACTION_NAME);
        if(!$auth_result){
            $params->error($auth->getErroeMsg());
        }
    }
}

以後咱們要創建一個抽象類Application/Home/Controller/BaseController.class.php繼承自Controller代碼以下:

<?php
/**
 * Created by PhpStorm.
 * User: rojer
 * Date: 16/4/5
 * Time: 下午9:28
 */

namespace Home\Controller;


use Think\Controller;

abstract class BaseController extends Controller
{

    abstract function getTargetModel();

    protected $auth = null;
    public function setAuth($self){
        $this->auth = $self;
    }

    function _initialize(){
        \Think\Hook::listen('auth', $this);
    }

    public function error($data,$status=-1){
        $this->ajaxReturn(array('msg'=>$data,'status'=>$status));
    }
}

最後咱們把Application/Home/Controller/IndexController.class.php繼承自BaseController並實現getTargetModel方法便可:

<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends BaseController {
    public function index(){
    }

    function getTargetModel()
    {
        return array('test',I('a'),'uid');
    }
}

在數據庫中,咱們插入

INSERT INTO `rule` (`id`, `action`, `name`, `condition`, `type`)
VALUES
    (1, 'Index::index', '測試', '{nick}=\'1\'', 0);

以後訪問http://localhost/news/1就能看到

{"msg": "請輸入您的accesstoken!", "status": -1}

的提示。

基礎Auth部分完畢
Git地址:https://github.com/enjory520/shizai.git

相關文章
相關標籤/搜索