說在前面的話: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:http://www.thinkphp.cn/down.html
thinphp文檔地址:http://www.kancloud.cn/manual/thinkphp/1...
而後把它放到本地服務器下,我用的是MAMP,window系統建議使用xmapp
(如不會請自行百度,xmapp已經算傻瓜操做了,下載就能用,還附帶mysql和phpmyadmin)
訪問http://localhost
就能夠看到Application目錄下面多了Home目錄,之後咱們主要在這下面進行開發。
而後你須要作的是填寫好數據庫的信息,與數據創建鏈接
在配置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