先來看官方提供的流程圖:
客戶端:
小程序客戶端通過 wx.login() 獲取登錄code , 然后將code當做參數傳遞到服務器、
getToken(){ var that = this; wx.login({ success: function (res) { wx.request({ url: 'http://wx.shop.com/api/v1/token', //服務器獲取token的api method:'POST', data: { code : res.code }, success: function (res) { console.log(res) } }) } }) }
服務器端(ThinkPHP5.1):
1. 根據客戶端傳遞的 code , 請求微信提供 api 換取該用戶的 openid 和 session_key ,同一個用戶在同一個小程序中的 openid 是不變的
<?php namespace app\common\service; use app\common\model\User; /** * UserToken類用作頒發微信令牌,繼承與Token */ class UserToken extends Token { protected $code; protected $Appid; protected $AppSecret; protected $LoginUrl; public function __construct($code) { $this->code = $code; //從服務器換取 openid 需要傳遞三個參數 // Appid、Appsecret、Code $this->Appid = config('wx.app_id'); $this->AppSecret = config('wx.app_secret'); //sprintf的作用是將字符串中占位符用特定值按順序替換 $this->LoginUrl = sprintf(config('wx.login_url'), $this->Appid, $this->AppSecret, $this->code); } /** * 根據用戶傳遞 code 去微信服務器換取 openid */ public function get() { $result = curl_get($this->LoginUrl); $wxResult = json_decode($result, true); if (empty($wxResult)) { throw new Exception("獲取session_key和open_id失敗,微信內部錯誤"); } //驗證獲取令牌是否成功 if (array_key_exists('errcode', $wxResult)) { throw new \app\common\exception\BaseException([ 'errorCode' => $wxResult['errcode'], 'msg' => $wxResult['errmsg'], ]); } else { return $this->grantToken($wxResult['openid']); } } /** * 頒發令牌 並將用戶信息序列化成json,已token為鍵保存在本地緩存 * 作用是 當用戶不存在時創建用戶 存在時返回用戶 id */ private function grantToken($openid) { //查找User表,查看該openid對應用戶是否存在,如是則返回uid,否則生成新用戶,返回uid $user = User::where('openid', $openid)->find(); if (!$user) { $uid = User::create([ 'openid' => $openid, ]); } else { $uid = $user->id; } //存入緩存 key:生成返回客戶端的令牌 value:openid + uid $key = $this->generateToken(); $cache_value['openid'] = $openid; $cache_value['uid'] = $uid; $expire = config('token.expire'); if (!cache($key, $cache_value, $expire)) { throw new Exception("緩存客戶令牌時出現錯誤"); } else { return $key; } } }
使用前需要先定位配置文件
<?php return [ // +--------------------------------- // 微信相關配置 // +--------------------------------- // 小程序app_id 'app_id' => 'wx0a1d95f443204af2', // 小程序app_secret 'app_secret' => 'a29462308699ae469d5fb6cc54a9a95a', // 微信使用code換取用戶openid及session_key的url地址 'login_url' => "https://api.weixin.qq.com/sns/jscode2session?" . "appid=%s&secret=%s&js_code=%s&grant_type=authorization_code", // 微信獲取access_token的url地址 'access_token_url' => "https://api.weixin.qq.com/cgi-bin/token?" . "grant_type=client_credential&appid=%s&secret=%s", //支付狀態 'unpaid' => 1, 'paid' => 2, 'shipped' => 3, ];
基類Token
<?php namespace app\common\service; use think\Facade\Request; // 這里將token的相關操作放在service層 // 定義了一個Token基類,基類中存放生成 token、根據token從緩存中獲取用戶數據操作 class Token { /** * 生成隨機字符串 作為 token */ public function generateToken() { $randChar = getRandChar(32); $timestamp = $_SERVER['REQUEST_TIME_FLOAT']; return md5($randChar . $timestamp); } /** * 根據用戶攜帶的 token ,從緩存中讀取用戶信息 */ public static function getCurrentIdentity() { $token = Request::header('token'); if (!$token) { throw new \app\common\exception\BaseException(['msg' => '請先登錄']); } $identity = cache($token); if ($identity) { return $identity; } else { throw new \app\common\exception\BaseException(['msg' => '身份已過期,請重新登錄']); } } /** * 獲得保存在緩存指定鍵的值 */ public static function getCurrentTokenVar($var) { $indentity = self::getCurrentIdentity(); if (!$indentity[$var]) { throw new Exception(['msg' => '嘗試獲取的Token變量並不存在']); } else { return $indentity[$var]; } } }
控制器中使用
class Token extends Controller { public function getToken() { $code = $this->request->param('code'); (new TokenValidate())->getToken($code); $ut = new UserToken($code); return json([ 'token' => $ut->get(), ]); } }
微信支付等操作都依賴於 openid