微信小程序非開放接口開發(fā)皇忿,token使用

前言:

之前介紹了些小程序用戶登錄獲取服務器token〕氩洌現在來介紹下用戶拿到token后請求一些權限接口的時候怎么,我們服務器端應該如何處理鳍烁。今天就用一個商城里非常常見的地址添加接口來舉例叨襟。

目錄:

  1. 思路圖
  2. token的接收和使用
  3. 接受地址數據,過濾地址數據
  4. 判斷是新增還是修改幔荒,保存數據
一:思路圖
token的使用.png
二:token的接收和使用

我們就從客戶端傳來token糊闽,地址數據梳玫,開始說起。

  1. 首先作為非開放接口右犹,我們第一步當然就是接收token提澎,如果沒有token,就直接拋出異常念链。
    那么盼忌,我們約定,token是在http請求中的headr傳遞過來掂墓。那我們首先取出token谦纱,然后到服務器緩存中查找是否有相對于的value值。如果沒有說明token不存在或者過期了君编,拋出異常跨嘉。

首先我們在我們的service層下的token服務中構建一個通用的獲取token對應儲存的值的數據方法。因為我們token對應的值中有好幾個數據吃嘿,有用戶的id祠乃,微信的openid,session_key。我們需要哪個字段的值唠椭,就傳入這個key就可以了倍谜。

    /**
     * 獲取token對應的某個數據
     * @param $key
     */
    protected static function getCurrentTokenValue($key)
    {
        //取出token
        $token = request()->header('token');
        //到緩存中獲取
        $value = Cache::get($token);
        //如果沒有這條數據
        if (empty($value)) {
            //拋出異常
            throw new TokenException();
        }  
    }
  1. 如果緩存中有值羔味,我們因為在存儲的時候是將數組序列話存儲的蜜葱。那么我這里就將數據反序列化一下祠够。當然,我還加入了一點容錯機制力崇,因為我是直接使用tp5提供的文件緩存斗塘,所以只能存儲字符串。如果今后我換了redis或者別的緩存驅動亮靴,可以存儲數組或者對象什么的馍盟。就不用序列化了。所以我在反序列化之前茧吊,先判斷下是不是數組贞岭,如果不是數組那就反序列化
    protected static function getCurrentTokenValue($key)
    {
        //取出token
        $token = request()->header('token');
        $value = Cache::get($token);
        if (empty($value)) {
            throw new TokenException();
        }
        //判斷是否是以數組形式儲存的
        if (!is_array($value)) {
            $value = unserialize($value);
        }
    }
  1. 最后一步就很簡單了,根據傳入的key值搓侄,去找$value中有沒有瞄桨,如果有就返回,如果沒有就拋出異常
    下面是完整代碼
    /**
     * 獲取token對應的某個數據
     * @param $key
     * @return mixed
     * @throws TokenException
     * @throws Exception
     */
    protected static function getCurrentTokenValue($key)
    {
        //取出token
        $token = request()->header('token');
        $value = Cache::get($token);
        if (empty($value)) {
            throw new TokenException();
        }
        //判斷是否是以數組形式儲存的
        if (!is_array($value)) {
            $value = unserialize($value);
        }
        if (array_key_exists($key, $value)) {
            return $value[$key];
        } else {
            throw new Exception('嘗試請求的參數并不存在');
        }
    }

好了 寫好這個通用的獲取token對應的值的方法之后呢讶踪,我們就需要想想我們到底需要這個token中的什么數據呢?既然是地址添加接口芯侥,那么就一定繞不開用戶id,不然怎么知道是誰的地址呢?那么我們就需要寫一個獲取當前用戶id的方法

    /**
     * 獲取當前用戶的id
     * @return mixed
     */
    public static function getCurrentId()
    {
        $id = self::getCurrentTokenValue('id');
        return $id;
    }

方法非常簡單啊柱查,就是之前的通用方法的具體實現廓俭。這樣我們在控制器中使用這個getCurrenId方法就可以拿到用戶的id了

三:接受地址數據,過濾地址數據

這里的接受數據過濾數據唉工,其實主要是關于安全方面的考慮研乒。因為我們要將用戶發(fā)來的數據直接存入數據庫是非常危險的。現在我們將接收數據淋硝,驗證數據告嘲,過濾數據〗钡兀總結到一起。方便今后使用赋焕。這里介紹的也是一個非常通用的方法参歹。
大家一定知道在接收數據后我們都會使用驗證器來驗證這些數據。

AddressValidate::instance()->goCheck();

就像這樣隆判,就是我之前寫過的獨立驗證器犬庇。驗證如果不符合規(guī)則就會拋出異常。那么驗證通過的數據就一定安全嗎侨嘀?我們現在來看看我的驗證規(guī)則


驗證規(guī)則

因為用戶id我們從token對應的值中取了臭挽,所以我們不會從客戶端傳遞的數據中獲取用戶id也不會信任用戶傳遞的數據的。

那么我如果驗證通過我就去取得用戶傳遞的所有數據這樣安全嗎咬腕?不安全欢峰。一個不起眼的小細節(jié)就在這里。首先我們的確不從客戶端取用戶id涨共,也不取驗證用戶id纽帖。但是我們不能保證客戶端不會傳用戶id呢。如果他傳了所有的地址數據举反,通過驗證懊直,還多傳了一個用戶id怎么辦。所以我們不能全盤接收用戶傳遞的數據火鼻∈夷遥可是我也不想一個字段一個字段的去接收。那么就要寫個通用的方法

這個通用的方法我就寫到驗證器的基類里中的goCheck方法中魁索,因為每次接收用戶數據融撞,都會去驗證的,也就會去調用goCheck方法蛾默,那么我們就寫在這里面讓它功能更強大.

先說下這個過濾的思路懦铺。我們在寫驗證器的時候是根據我們要接收哪些字段我們就會驗證哪些字段對吧。那么我們要過濾出來的字段也就是我們驗證規(guī)則數組中的key支鸡,不知道大家注意到了沒有


驗證規(guī)則中的key

那么我們只需將$rule中的key值作為過濾依據冬念,驗證規(guī)則中有的我們就保留趁窃,驗證規(guī)則之外的不保留

    /**
     * 根據驗證器來獲取客戶端傳遞的信息
     * @param $arrays 傳入接收的客戶端所有數據
     * @return array
     * @throws Exception
     */
    protected function getDataByRule($arrays)
    {
        //如果傳遞過來的數據中包含user_id這個字段的話,那就十有八九這人是個黑帽子了
        if (array_key_exists('user_id', $arrays)) {
            throw new Exception('本接口不支持你這個user_id的參數急前,別想了兄弟');
        }
        $newArr = [];
        //遍歷驗證規(guī)則數組
        foreach ($this->rule as $key => $value) {
            //規(guī)則中有的key值醒陆,對應客戶端的所有數據中的key值。保存在$newArrr中
            $newArr[$key] = $arrays[$key];
        }
        return $newArr;
    }

這個方法寫好之后裆针,我么說過要將它寫到goCheck方法中刨摩,讓驗證數據的同時,過濾了數據世吨。我們就在驗證成功后調用過濾方法澡刹。不清楚這個驗證器的使用的可以回顧一下

    /**
     * 獲取傳遞參數,并驗證
     * @return bool|array
     * @throws ParameterException
     */
    public function goCheck()
    {
        //接收參數
        $request = Request::instance();
        //通過param方法獲取到所有的參數
        $params = $request->param();
        //由哪個對象來調用goCheck方法,就是由哪個對象來調用check方法,將接收的所有參數傳遞進去
        $result = $this->batch()->check($params);
        if (!$result) {
            //如果結果為false,調用getError方法獲取錯誤信息
            $error = $this->getError();
            //拋出參數錯誤異常
            throw new ParameterException(['msg' => $error]);
        } else {
            //調用獲取過濾參數的方法耘婚,返回給控制器
            return $this->getDataByRule($params);
        }
    }

那么現在罢浇,只需要在控制器中拿個變量接收下goCheck方法的返回值就行了

$data = AddressValidate::instance()->goCheck();
四:判斷是新增還是修改,保存數據

由于新增地址和修改地址的邏輯很類似 完全可以寫在一個接口中沐祷。
之前通過token獲取到對應的用戶id嚷闭,我們還需要為程序的健壯性,再去查詢下這個用戶是否是存在赖临,是不是被我們刪除了或者怎么了胞锰。
所有在控制器中調用模型上的getUserById方法

  //驗證用戶是否存在
  $user = User::getUserById($id);

模型方法也很簡單

    /**
     * 通過用戶id,查詢用戶
     * @param $id
     * @return bool|null|static
     */
    public static function getUserById($id)
    {
        $result = self::get($id);
        if (empty($result)) {
            return false;
        } else {
            //用戶存在兢榨,返回數據模型對象
            return $result;
        }
    }

順便在User模型中將地址UserAddress關聯起來(關聯就不詳細介紹了嗅榕。有時間單獨寫)

    //一對一關聯,外鍵在外
   public function address()
    {
        return $this->hasOne('UserAddress','user_id','id');
    }

好了吵聪,現在我們通過判斷用戶是否存在順便也取到了用戶的數據模型對象誊册。也關聯好了地址表
下一步我們就判斷地址是否存在,然后保存數據就好了

        //判斷用戶是新增還是修改
        $address = $user->address();
        //返回關聯的對象hasOne對象
        if (empty($address)) {
            //調用hasOne對象上的保存(新增)當前關聯數據對象方法
            $result = $address->save($data);
        } else {
            //address屬性中保存的是UserAddress數據對象暖璧,是model的子類案怯。調用model上的save方法
            $result = $user->address->save($data);
        }
        //如果保存成功
        if (!empty($result)) {
            //返回一個操作成功的對象,對象里包含成功的信息
            return new SuccessMessage();
        } else {
            //拋出異常
            throw new Exception('保存地址失敗');
        }
細節(jié)

值得注意的是這里澎办,新增和修改的save方法是不同的嘲碱。我也將$user->address()和$user->address打印出來看了。帶括號的是hasOne對象也就我們關聯的時候返回的那個對象局蚀。不帶括號的是model對象麦锯,就是我創(chuàng)建的UserAddress類的對象。

我的理解是琅绅,當關聯地址表中沒有這個用戶的數據扶欣,返回的hasOne中的data屬性就會是個空數組。來判斷數據是否存在。我們就可以調用關聯對象也就是(hasOne)對象上的save方法料祠。這里是創(chuàng)建一個關聯上user的數據對象的意思骆捧。
當返回的關聯對象含有數據,那么使用user模型上的address屬性髓绽,address屬性上存儲的其實就是UserAddress模型對象敛苇,我們打印出來也印證了這個觀點。那么既然是模型對象model的子類顺呕,那么直接使用model中最常用的save方法枫攀,將數據存入,就可以完成修改了株茶。

這里比較繞哈来涨,雖然可以用模型直接新增,傳入用戶id的方法完成這個業(yè)務启盛,但是這個方法我之前沒有怎么用過扫夜,還是給大家介紹下一個新的思路。

下面我附上完整的控制器代碼

   /**
     * 添加或者更新地址
     * @url http://local.jxshop.com/api/v1/address/add
     * @url http://local.jxshop.com/api/v1/address/update
     * @http GET
     *
     */
    public function createOrUpdateAddress()
    {
        $data = AddressValidate::instance()->goCheck();
        //驗證token真實性
        $id = TokenService::getCurrentId();
        //驗證用戶是否存在
        $user = User::getUserById($id);
        if (!$user) {
            throw new UserException();
        }
        //判斷用戶是新增還是修改
        $address = $user->address();
        //返回關聯的對象hasOne對象
        if (empty($address)) {
            //調用hasOne對象上的保存(新增)當前關聯數據對象方法
            $result = $address->save($data);
        } else {
            //address屬性中保存的是UserAddress數據對象驰徊,是model的子類。調用model上的save方法
            $result = $user->address->save($data);
        }
        //如果保存成功
        if (!empty($result)) {
            //返回一個操作成功的對象堕阔,對象里包含成功的信息
            return new SuccessMessage();
        } else {
            //拋出異常
            throw new Exception('保存地址失敗');
        }
    }

那么今天的非開放接口就介紹到這里了棍厂。有哪些不對的地方,希望大神能夠指正超陆,我共同學習牺弹。

以上

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市时呀,隨后出現的幾起案子张漂,更是在濱河造成了極大的恐慌,老刑警劉巖谨娜,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件航攒,死亡現場離奇詭異,居然都是意外死亡趴梢,警方通過查閱死者的電腦和手機漠畜,發(fā)現死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來坞靶,“玉大人憔狞,你說我怎么就攤上這事≌靡酰” “怎么了瘾敢?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我簇抵,道長庆杜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任正压,我火速辦了婚禮欣福,結果婚禮上,老公的妹妹穿的比我還像新娘焦履。我一直安慰自己拓劝,他們只是感情好,可當我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布嘉裤。 她就那樣靜靜地躺著郑临,像睡著了一般。 火紅的嫁衣襯著肌膚如雪屑宠。 梳的紋絲不亂的頭發(fā)上厢洞,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天,我揣著相機與錄音典奉,去河邊找鬼躺翻。 笑死,一個胖子當著我的面吹牛卫玖,可吹牛的內容都是我干的公你。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼假瞬,長吁一口氣:“原來是場噩夢啊……” “哼陕靠!你這毒婦竟也來了?” 一聲冷哼從身側響起脱茉,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤剪芥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后琴许,有當地人在樹林里發(fā)現了一具尸體税肪,經...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年榜田,在試婚紗的時候發(fā)現自己被綠了寸认。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡串慰,死狀恐怖偏塞,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情邦鲫,我是刑警寧澤灸叼,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布神汹,位于F島的核電站,受9級特大地震影響古今,放射性物質發(fā)生泄漏屁魏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一捉腥、第九天 我趴在偏房一處隱蔽的房頂上張望氓拼。 院中可真熱鬧,春花似錦抵碟、人聲如沸桃漾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽撬统。三九已至,卻和暖如春敦迄,著一層夾襖步出監(jiān)牢的瞬間恋追,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工罚屋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留苦囱,地道東北人。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓脾猛,卻偏偏與公主長得像撕彤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子尖滚,可洞房花燭夜當晚...
    茶點故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現瞧柔,斷路器漆弄,智...
    卡卡羅2017閱讀 134,628評論 18 139
  • 22年12月更新:個人網站關停,如果仍舊對舊教程有興趣參考 Github 的markdown內容[https://...
    tangyefei閱讀 35,165評論 22 257
  • 國家電網公司企業(yè)標準(Q/GDW)- 面向對象的用電信息數據交換協(xié)議 - 報批稿:20170802 前言: 排版 ...
    庭說閱讀 10,915評論 6 13
  • iOS網絡架構討論梳理整理中造锅。撼唾。。 其實如果沒有APIManager這一層是沒法使用delegate的哥蔚,畢竟多個單...
    yhtang閱讀 5,172評論 1 23
  • 頻繁翻閱文字中倒谷,領略各色文章風采。 有些文字糙箍,覺得自己是能夠寫出來的渤愁;有些文字,一見驚心深夯,卻也心下了然抖格,這輩子怕也...
    余應心閱讀 539評論 0 1