近排在做微信介面開發,所以總結一下微信授權登入並取得使用者資訊 這個介面的開發流程。
一、首先你的微信公眾號要取得對應的AppID和AppSecret,申請微信登入且通過審核後,才可開始存取流程。
二、授權流程
1、流程說明
(1). 第三方發起微信授權登入要求,微信使用者允許授權第三方應用後,微信會拉起應用程式或重定向到第三方網站,並且帶上授權臨時票據code參數;
(2). 透過code參數加上AppID和AppSecret等,透過API換取access_token;
(3). 透過access_token進行介面調用,取得使用者基本資料資源或協助使用者實現基本操作。
2、取得access_token時序圖:
#三、開發(我的用是CI框架,其實什麼框架都一樣,MVC模式就行了)
1、請求CODE
weixin.php
<?php class weixinController extends CI_Controller { public $userInfo; public $wxId; public function __construct(){ parent::__construct(); //只要用户一访问此模块,就登录授权,获取用户信息 $this->userInfo = $this->getWxUserInfo(); } /** * 确保当前用户是在微信中打开,并且获取用户信息 * * @param string $url 获取到微信授权临时票据(code)回调页面的URL */ private function getWxUserInfo($url = '') { //微信标记(自己创建的) $wxSign = $this->input->cookie('wxSign'); //先看看本地cookie里是否存在微信唯一标记, //假如存在,可以通过$wxSign到redis里取出微信个人信息(因为在第一次取到微信个人信息,我会将其保存一份到redis服务器里缓存着) if (!empty($wxSign)) { //如果存在,则从Redis里取出缓存了的数据 $userInfo = $this->model->redisCache->getData("weixin:sign_{$wxSign}"); if (!empty($userInfo)) { //获取用户的openid $this->wxId = $userInfo['openid']; //将其存在cookie里 $this->input->set_cookie('wxId', $this->wxId, 60*60*24*7); return $userInfo; } } //获取授权临时票据(code) $code = $_GET['code']; if (empty($code)) { if (empty($url)) { $url = rtirm($_SERVER['QUERY_STRING'], '/'); //到WxModel.php里获取到微信授权请求URL,然后redirect请求url redirect($this->model->wx->getOAuthUrl(baseUrl($url))); } } } } ?>
取得code的Controller程式碼
# Wxmodel.php
<?php class WxModel extends ModelBase{ public $appId; public $appSecret; public $token; public function __construct() { parent::__construct(); //审核通过的移动应用所给的AppID和AppSecret $this->appId = 'wx0000000000000000'; $this->appSecret = '00000000000000000000000000000'; $this->token = '00000000'; } /** * 获取微信授权url * @param string 授权后跳转的URL * @param bool 是否只获取openid,true时,不会弹出授权页面,但只能获取用户的openid,而false时,弹出授权页面,可以通过openid获取用户信息 * */ public function getOAuthUrl($redirectUrl, $openIdOnly, $state = '') { $redirectUrl = urlencode($redirectUrl); $scope = $openIdOnly ? 'snsapi_base' : 'snsapi_userinfo'; $oAuthUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid={$this->appId}&redirect_uri={$redirectUrl}&response_type=code&scope=$scope&state=$state"; return $oAuthUrl; } 获取code的Model代码
取得code的Model程式碼
這裡附上請求參數說明和回傳值說明
請求參數說明:
回應回傳值說明:
# 當請求成功,會redirect到請求參數中的redirect_uri的值中去,其實又回到weixin.php的$this->userInfo = $this->getWxUserInfo();這行去,然後再一次進入到getWxUserInfo()方法,此時
//获取授权临时票据(code) $code = $_GET['code'];
這行也已經能取得得到code的值了。接著進行第二步。
2、透過code取得access_token
weixin.php
userInfo = $this->getWxUserInfo(); } /** * 确保当前用户是在微信中打开,并且获取用户信息 * * @param string $url 获取到微信授权临时票据(code)回调页面的URL */ private function getWxUserInfo($url = '') { //微信标记(自己创建的) $wxSign = $this->input->cookie('wxSign'); //先看看本地cookie里是否存在微信唯一标记, //假如存在,可以通过$wxSign到redis里取出微信个人信息(因为在第一次取到微信个人信息,我会将其保存一份到redis服务器里缓存着) if (!empty($wxSign)) { //如果存在,则从Redis里取出缓存了的数据 $userInfo = $this->model->redisCache->getData("weixin:sign_{$wxSign}"); if (!empty($userInfo)) { //获取用户的openid $this->wxId = $userInfo['openid']; //将其存在cookie里 $this->input->set_cookie('wxId', $this->wxId, 60*60*24*7); return $userInfo; } } //获取授权临时票据(code) $code = $_GET['code']; if (empty($code)) { if (empty($url)) { $url = rtirm($_SERVER['QUERY_STRING'], '/'); //到WxModel.php里获取到微信授权请求URL,然后redirect请求url redirect($this->model->wx->getOAuthUrl(baseUrl($url))); } } /***************这里开始第二步:通过code获取access_token****************/ $result = $this->model->wx->getOauthAccessToken($code); //如果发生错误 if (isset($result['errcode'])) { return array('msg'=>'授权失败,请联系客服','result'=>$result); } //到这一步就说明已经取到了access_token $this->wxId = $result['openid']; $accessToken = $result['access_token']; $openId = $result['openid']; //将openid和accesstoken存入cookie中 $this->input->set_cookie('wx_id', $this->wxId, 60*60*24*7); $this->input->set_cookie('access_token', $accessToken); 获取access_token的控制器代码
取得access_token的控制器程式碼
# WxModel.php
<?php class WxModel extends ModelBase{ public $appId; public $appSecret; public $token; public function __construct() { parent::__construct(); //审核通过的移动应用所给的AppID和AppSecret $this->appId = 'wx0000000000000000'; $this->appSecret = '00000000000000000000000000000'; $this->token = '00000000'; } /** * 获取微信授权url * @param string 授权后跳转的URL * @param bool 是否只获取openid,true时,不会弹出授权页面,但只能获取用户的openid,而false时,弹出授权页面,可以通过openid获取用户信息 * */ public function getOAuthUrl($redirectUrl, $openIdOnly, $state = '') { $redirectUrl = urlencode($redirectUrl); $scope = $openIdOnly ? 'snsapi_base' : 'snsapi_userinfo'; $oAuthUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid={$this->appId}&redirect_uri={$redirectUrl}&response_type=code&scope=$scope&state=$state#wechat_redirect"; return $oAuthUrl; } /** * 获取access_token */ public function getoAuthAccessToken($code) { return json_decode(file_get_contents("https://api.weixin.qq.com/sns/oauth2/access_token?appid={$this->AppId}&secret={$this->AppSecret}&code={$authCode}&grant_type=authorization_code",true); } 获取access_token的Model代码
取得access_token的Model程式碼
這裡附上參數說明
請求參數說明:
回應回傳值說明:
當回傳錯誤時是這樣子的:
##
取得access_token後,進行介面調用,有下列前提:
(1) access_tokec有效且未逾時;
(2)微信使用者已授權給第三方應用帳號對應的介面作用域(scope)。
以下是取得使用者資訊的程式碼
weixin.phpuserInfo = $this->getWxUserInfo();
}
/**
* 确保当前用户是在微信中打开,并且获取用户信息
*
* @param string $url 获取到微信授权临时票据(code)回调页面的URL
*/
private function getWxUserInfo($url = '') {
//微信标记(自己创建的)
$wxSign = $this->input->cookie('wxSign');
//先看看本地cookie里是否存在微信唯一标记,
//假如存在,可以通过$wxSign到redis里取出微信个人信息(因为在第一次取到微信个人信息,我会将其保存一份到redis服务器里缓存着)
if (!empty($wxSign)) {
//如果存在,则从Redis里取出缓存了的数据
$userInfo = $this->model->redisCache->getData("weixin:sign_{$wxSign}");
if (!empty($userInfo)) {
//获取用户的openid
$this->wxId = $userInfo['openid'];
//将其存在cookie里
$this->input->set_cookie('wxId', $this->wxId, 60*60*24*7);
return $userInfo;
}
}
//获取授权临时票据(code)
$code = $_GET['code'];
if (empty($code)) {
if (empty($url)) {
$url = rtirm($_SERVER['QUERY_STRING'], '/');
//到WxModel.php里获取到微信授权请求URL,然后redirect请求url
redirect($this->model->wx->getOAuthUrl(baseUrl($url)));
}
}
/***************这里开始第二步:通过code获取access_token****************/
$result = $this->model->wx->getOauthAccessToken($code);
//如果发生错误
if (isset($result['errcode'])) {
return array('msg'=>'授权失败,请联系客服','result'=>$result);
}
//到这一步就说明已经取到了access_token
$this->wxId = $result['openid'];
$accessToken = $result['access_token'];
$openId = $result['openid'];
//将openid和accesstoken存入cookie中
$this->input->set_cookie('wx_id', $this->wxId, 60*60*24*7);
$this->input->set_cookie('access_token', $accessToken);
/*******************这里开始第三步:通过access_token调用接口,取出用户信息***********************/
$this->userInfo = $this->model->wx->getUserInfo($openId, $accessToken);
//自定义微信唯一标识符
$wxSign =substr(md5($this->wxId.'k2a5dd'), 8, 16);
//将其存到cookie里
$this->input->set_cookie('wxSign', $wxSign, 60*60*24*7);
//将个人信息缓存到redis里
$this->library->redisCache->set("weixin:sign_{$wxSign}", $userInfo, 60*60*24*7);
return $userInfo;
}
}
?>
获取用户信息的Controller
WxModel.php<?php
class WxModel extends ModelBase{
public $appId;
public $appSecret;
public $token;
public function __construct() {
parent::__construct();
//审核通过的移动应用所给的AppID和AppSecret
$this->appId = 'wx0000000000000000';
$this->appSecret = '00000000000000000000000000000';
$this->token = '00000000';
}
/**
* 获取微信授权url
* @param string 授权后跳转的URL
* @param bool 是否只获取openid,true时,不会弹出授权页面,但只能获取用户的openid,而false时,弹出授权页面,可以通过openid获取用户信息
*
*/
public function getOAuthUrl($redirectUrl, $openIdOnly, $state = '') {
$redirectUrl = urlencode($redirectUrl);
$scope = $openIdOnly ? 'snsapi_base' : 'snsapi_userinfo';
$oAuthUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid={$this->appId}&redirect_uri={$redirectUrl}&response_type=code&scope=$scope&state=$state#wechat_redirect";
return $oAuthUrl;
}
/**
* 获取access_token
*/
public function getoAuthAccessToken($code) {
return json_decode(file_get_contents("https://api.weixin.qq.com/sns/oauth2/access_token?appid={$this->AppId}&secret={$this->AppSecret}&code={$authCode}&grant_type=authorization_code",true);
}
/**
* 获取用户信息
*/
public function getUserInfo($openId, $accessToken) {
$url = 'https://api.weixin.qq.com/sns/userinfo';
//获取用户微信账号信息
$userInfo = $this->callApi("$url?access_token=$accessToken&openid=$openId&lang=zh-CN");
if ($userInfo['errcode']) {
return array('msg'=>'获取用户信息失败,请联系客服', $userInfo);
}
$userInfo['wx_id'] = $openId;
return $userInfo;
}
/**
* 发起Api请求,并获取返回结果
* @param string 请求URL
* @param mixed 请求参数 (array|string)
* @param string 请求类型 (GET|POST)
* @return array
*/
public function callApi($apiUrl, $param = array(), $method = 'GET') {
$result = curl_request_json($error, $apiUrl, $params, $method);
//假如返回的数组有错误码,或者变量$error也有值
if (!empty($result['errcode'])) {
$errorCode = $result['errcode'];
$errorMsg = $result['errmsg'];
} else if ($error != false) {
$errorCode = $error['errorCode'];
$errorMsg = $error['errorMessage'];
}
if (isset($errorCode)) {
//将其插入日志文件
file_put_contents("/data/error.log", "callApi:url=$apiUrl,error=[$errorCode]$errorMsg");
if ($errorCode === 40001) {
//尝试更正access_token后重试
try {
$pos = strpos(strtolower($url), 'access_token=');
if ($pos !==false ) {
$pos += strlen('access_token=');
$pos2 = strpos($apiUrl, '&' ,$pos);
$accessTokened = substr($apiUrl, $pos, $pos2 === false ? null : ($pos2 - $pos));
return $this->callApi(str_replace($accessTokened, $this->_getApiToken(true), $apiUrl), $param, $method);
}
}catch (WeixinException $e) {
}
}
//这里抛出异常,具有的就不详说了
throw new WeixinException($errorMessage, $errorCode);
}
return $result;
}
/**
* 获取微信 api 的 access_token 。 不同于 OAuth 中的 access_token ,参见 http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96access_token
*
* @param bool 是否强制刷新 accessToken
*/
private function _getApiToken($forceRefresh = false) {
//先查看一下redis里是否已经缓存过access_token
$accessToken = $this->library->redisCache->get('Weixin:AccessToken');
if($forceRefresh || empty($accessToken)) {
$result = $this->callApi("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->appId}&secret={$this->appSecret}");
$accessToken = $result['access_token'];
$expire = max(1, intval($result['expires_in']) - 60);
//将access_token缓存到redis里去
$this->library->redisCache->set('Weixin:AccessToken', $accessToken, $expire);
}
return $accessToken;
}
?>
Common.php 透過以上三步驟呼叫接口,就可以取得到使用者的微信帳號資訊了。 更多微信授權登入並取得使用者資訊介面相關文章請關注PHP中文網! <?php
/**
* 发起一个HTTP(S)请求,并返回json格式的响应数据
* @param array 错误信息 array($errorCode, $errorMessage)
* @param string 请求Url
* @param array 请求参数
* @param string 请求类型(GET|POST)
* @param int 超时时间
* @param array 额外配置
*
* @return array
*/
public function curl_request_json(&$error, $url, $param = array(), $method = 'GET', $timeout = 10, $exOptions = null) {
$error = false;
$responseText = curl_request_text($error, $url, $param, $method, $timeout, $exOptions);
$response = null;
if ($error == false && $responseText > 0) {
$response = json_decode($responseText, true);
if ($response == null) {
$error = array('errorCode'=>-1, 'errorMessage'=>'json decode fail', 'responseText'=>$responseText);
//将错误信息记录日志文件里
$logText = "json decode fail : $url";
if (!empty($param)) {
$logText .= ", param=".json_encode($param);
}
$logText .= ", responseText=$responseText";
file_put_contents("/data/error.log", $logText);
}
}
return $response;
}
/**
* 发起一个HTTP(S)请求,并返回响应文本
* @param array 错误信息 array($errorCode, $errorMessage)
* @param string 请求Url
* @param array 请求参数
* @param string 请求类型(GET|POST)
* @param int 超时时间
* @param array 额外配置
*
* @return string
*/
public function curl_request_text(&$error, $url, $param = array(), $method = 'GET', $timeout = 15, $exOptions = NULL) {
//判断是否开启了curl扩展
if (!function_exists('curl_init')) exit('please open this curl extension');
//将请求方法变大写
$method = strtoupper($method);
$ch = curl_init();
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_HEADER, false);
if (isset($_SERVER['HTTP_USER_AGENT'])) curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
if (isset($_SERVER['HTTP_REFERER'])) curl_setopt($ch, CURLOPT_REFERER, $_SERVER['HTTP_REFERER']);
curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
switch ($method) {
case 'POST':
curl_setopt($ch, CURLOPT_POST, true);
if (!empty($param)) {
curl_setopt($ch, CURLOPT_POSTFIELDS, (is_array($param)) ? http_build_query($param) : $param);
}
break;
case 'GET':
case 'DELETE':
if ($method == 'DELETE') {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
}
if (!empty($param)) {
$url = $url.(strpos($url, '?') ? '&' : '?').(is_array($param) ? http_build_query($param) : $param);
}
break;
}
curl_setopt($ch, CURLINFO_HEADER_OUT, true);
curl_setopt($ch, CURLOPT_URL, $url);
//设置额外配置
if (!empty($exOptions)) {
foreach ($exOptions as $k => $v) {
curl_setopt($ch, $k, $v);
}
}
$response = curl_exec($ch);
$error = false;
//看是否有报错
$errorCode = curl_errno($ch);
if ($errorCode) {
$errorMessage = curl_error($ch);
$error = array('errorCode'=>$errorCode, 'errorMessage'=>$errorMessage);
//将报错写入日志文件里
$logText = "$method $url: [$errorCode]$errorMessage";
if (!empty($param)) $logText .= ",$param".json_encode($param);
file_put_contents('/data/error.log', $logText);
}
curl_close($ch);
return $response;
}
?>