我们利用微信小程序给每一位用户生成的Code数字码,把这个Code码传递给我们的getToken接口,这个Code码是我们在微信服务器换取用户信息的标识,我们在getToken接口中接收到这个Code码之后,我们就需要向微信服务器发送一个请求并将Code码发送到微信服务器中,然后微信服务器就会返回一个openid和一个session_key,这个openid就是我们需要的用户身份的唯一标识,而且如果我们需要使用微信支付时,这个openid也是必需的。当我们获取了openid后,我们需要将其存到我们的的数据库中。而openid是一个比较隐秘的用户数据而且是固定不变且长久有效的,我们并不提倡将其返回到客户端去,解决这个问题的方法是生成一个具有有效期限的Token令牌返回到客户端中,下次访问时只需携带这个令牌并通过这个令牌找到对应的openid从而间接地获取openid,而Token令牌的存储,我们将其存放在缓存中,如果我们将其存放在数据库中,我们访问接口时,会对数据库产生一个相当大的压力,且大多数情况下,缓存的访问速度是要优于数据库的访问速度的。
Controller/Token.php
<?php
namespace app\api\controller\v1;
use app\api\service\UserToken;
use app\api\validate\TokenGet;
class Token
{
public function getToken($code = '')
{
(new TokenGet())->goCheck();
$ut = new UserToken($code);
$token = $ut->get();
return [
'token' =>$token
];
}
}
service/Token.php
<?php
namespace app\api\service;
use app\lib\exception\TokenException;
use think\Cache;
use think\Exception;
use think\Request;
class Token
{
//生成令牌
public static function generateToken(){
//32个字符组成一组随机字符串
$rangChars = getRandChar(32);
//用三组字符串,进行md5加密
$timestamp = $_SERVER['REQUEST_TIME_FLOAT'];
//salt 盐
$salt = config('secure.token_salt');
return md5($rangChars.$timestamp.$salt);
}
public static function getCurrentTokenVar($key){
$token = Request::instance()
->header('token');//tp5的Request::instance()获取post、get、参数、表单上传的文件
$vars = Cache::get($token);
if(!$vars){
throw new TokenException();
}else{
if(!is_array($vars)){
$vars = json_decode($vars,true);
}
if(array_key_exists($key,$vars)){
return $vars[$key];
}else{
throw new Exception('尝试获取的Token变量并不存在');
}
}
}
//由缓存的token获取Uid
public static function getCurrentUid(){
$uid = self::getCurrentTokenVar('uid');
return $uid;
}
}
service/UserToken.php
<?php
namespace app\api\service;
use app\lib\exception\TokenException;
use app\lib\exception\WeChatException;
use think\Exception;
use app\api\model\User as UserModel;
class UserToken extends Token
{
protected $code;
protected $wxAppID;
protected $wxAppSecret;
protected $wxLoginUrl;
function __construct($code)
{
$this->code = $code;
$this->wxAppID = config('wx.app_id');
$this->wxAppSecret = config('wx.app_secret');
//sprintf函数 把url中的s% 替换成想要的数据
$this->wxLoginUrl = sprintf(config('wx.login_url'),
$this->wxAppID,$this->wxAppSecret,$this->code);
}
public function get(){
$result = curl_get($this->wxLoginUrl);
$wxResult = json_decode($result,true);//result 变成json字符串
if(empty($wxResult)){
throw new Exception('获取session_key及openID时异常');
}else{
$loginFail = array_key_exists('errcode',$wxResult);
// return $loginFail;
if($loginFail){
$this->processLoginError($wxResult);
}else{
return $this->grantToken($wxResult);
}
}
}
private function grantToken($wxResult){
//拿到openid
//数据库里看一下,这个openid是不是已经存在
//如果存在,则不处理,如果不存在那么新增一条user记录
//生成令牌,准备缓存数据,写入缓存
//把令牌返回到客户端去
//key:令牌
//value:wxResult,uid,scope
$openid = $wxResult['openid'];
$user = UserModel::getByOpenID($openid);
if($user){
$uid = $user->id;
}else{
$uid = $this->newUser($openid);
}
$cachedValue = $this->prepareCachedValue($wxResult,$uid);
$token = $this->saveToCache($cachedValue);
return $token;
}
//写入缓存
private function saveToCache($cachedValue){
$key = self::generateToken();
$value = json_encode($cachedValue);//把数组转换成字符串
$expire_in = config('setting.token_expire_in');
$request = cache($key,$value,$expire_in);
if(!$request){
throw new TokenException([
'msg' => '服务器缓存异常',
'errorCode' => 10005
]);
}
return $key;
}
//准备value的一系列数据
private function prepareCachedValue($wxResult,$uid){
$cachedValue = $wxResult;
$cachedValue['uid'] = $uid;
$cachedValue['scope'] = 16;//权限高低2,4,6,8,12,14,16
return $cachedValue;
}
//openID不存在 则新增一条记录
private function newUser($openid){
$user = UserModel::create([
'openid' => $openid
]);
return $user->id;
}
//封装一个抛出异常的方法
private function processLoginError($wxResult){
throw new WeChatException(
[
'msg' => $wxResult['errmsg'],
'errorCode' => $wxResult['errcode']
]);
}
}
model/User.php
<?php
namespace app\api\model;
class User extends BaseModel
{
public static function getByOpenID($openid){
$user = self::where('openid','=',$openid)
->find();
return $user;
}
}
validate/BaseValidate
<?php
namespace app\api\validate;
use app\lib\exception\ParameterException;
use think\Exception;
use think\Request;
use think\Validate;
class BaseValidate extends Validate
{
public function goCheck()
{
$request = Request::instance();
$params = $request->param();
$result = $this->batch()->check($params);
if(!$result){
$e = new ParameterException([
'msg' => $this->error,
]);
throw $e;
}else{
return true;
}
}
protected function isPositiveInteger($value,$rule = '',
$data = '', $field = '')
{
if(is_numeric($value) && is_int($value + 0) && ($value + 0) > 0){
return true;
}else{
return false;
}
}
protected function isNotEmpty($value,$rule = '',
$data = '', $field = '')
{
if(empty($value)){
return false;
}else{
return true;
}
}
}
validate/TokenGet
<?php
namespace app\api\validate;
class TokenGet extends BaseValidate
{
protected $rule = [
'code' => 'require|isNotEmpty'
];
protected $message = [
'code' => '没有code'
];
}