先来看官方提供的流程图:
客户端:
小程序客户端通过 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