PHP微信三方平台-授权登录
一、逻辑步骤解析
步骤 1:第三方平台方获取预授权码(pre_auth_code)
步骤 2:引入用户进入授权页
第三方平台方可以在自己的网站中放置“微信公众号授权”或者“小程序授权”的入口,或生成授权链接放置在移动网页中,引导公众号和小程序管理员进入授权页。
方式一:授权注册页面扫码授权
授权页网址为:
https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid=xxxx&pre_auth_code=xxxxx&redirect_uri=xxxx&auth_type=xxx。
参数 |
必填 |
参数说明 |
component_appid |
是 |
第三方平台方 appid |
pre_auth_code |
是 |
预授权码 |
redirect_uri |
是 |
回调 URI |
auth_type |
否 |
要授权的帐号类型, 1 则商户扫码后,手机端仅展示公众号、2 表示仅展示小程序,3 表示公众号和小程序都展示。如果为未指定,则默认小程序和公众号都展示。第三方平台开发者可以使用本字段来控制授权的帐号类型。 |
biz_appid |
否 |
指定授权唯一的小程序或公众号 |
二、代码实现
wx_tools 工具文件
/** * 以post方式提交xml到对应的接口url * @param string $url 提交地址 * @param string $param 需要post的xml数据 * @param bool $file 是否上传文件 * @param bool|array $cert 是否需要证书,默认不需要 如果是数组代表有证书地址 请按以下格式 array('cert' => 'cert.pem', 'key' => 'key.pem', 'rootca' => 'rootca.pem'); * @param int $second * @return mixed */ public static function postCurl($url, $param, $file = false, $cert = false, $second = 30) { $curl = curl_init(); //设置超时 curl_setopt($curl, CURLOPT_TIMEOUT, $second); if (stripos($url, "https://") !== FALSE) { curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($curl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1 } if (PHP_VERSION_ID >= 50500 && class_exists('\CURLFile')) { $is_file = true; } else { $is_file = false; if (defined('CURLOPT_SAFE_UPLOAD')) { curl_setopt($curl, CURLOPT_SAFE_UPLOAD, false); } } if (is_string($param)) { $str_post = $param; } elseif ($file) { if ($is_file) { foreach ($param as $key => $val) { if (substr($val, 0, 1) == '@') { $param[$key] = new \CURLFile(realpath(substr($val, 1))); } } } $str_post = $param; } else { $post = array(); foreach ($param as $key => $val) { $post[] = $key . "=" . urlencode($val); } $str_post = join("&", $post); } //设置证书 todo 未验证 if (is_array($cert)) { //请确保您的libcurl版本是否支持双向认证,版本高于7.20.1 使用证书:cert 与 key 分别属于两个.pem文件 curl_setopt($curl, CURLOPT_SSLKEYTYPE, 'PEM'); curl_setopt($curl, CURLOPT_SSLCERT, $cert['cert']); curl_setopt($curl, CURLOPT_SSLKEYTYPE, 'PEM'); curl_setopt($curl, CURLOPT_SSLKEY, $cert['key']); //红包使用 if (empty($cert['rootca'])) { curl_setopt($curl, CURLOPT_SSLKEYTYPE, 'PEM'); curl_setopt($curl, CURLOPT_CAINFO, $cert['rootca']); } } curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_POST, true); curl_setopt($curl, CURLOPT_POSTFIELDS, $str_post); $content = curl_exec($curl); $status = curl_getinfo($curl); if (intval($status["http_code"]) == 200) { curl_close($curl); // ApiLog::setMessage(\Yii::$app->session->get('request_base_api_log_id'),['url' => $url, 'message'=> $content], 1); return $content; } else { $error = curl_errno($curl); curl_close($curl); // $this->err_code = $error; // $this->err_msg = $this->curl_error[$error]; // ApiLog::setMessage(\Yii::$app->session->get('request_base_api_log_id'),['url' => $url, 'message'=> $content], 0); return false; } } /** * CURL GET 请求 * @param $url * @return bool|mixed */ public static function getCurl($url) { $curl = curl_init(); if (stripos($url, "https://") !== FALSE) { curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE); curl_setopt($curl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1 } curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); $content = curl_exec($curl); $status = curl_getinfo($curl); if (intval($status["http_code"]) == 200) { curl_close($curl); // ApiLog::setMessage(\Yii::$app->session->get('request_base_api_log_id'),['url' => $url, 'message'=> $content], 1); return $content; } else { $error = curl_errno($curl); curl_close($curl); file_put_contents('../web/logs/notify/error' . date('YmdHi') . '.txt', $error); // ApiLog::setMessage(\Yii::$app->session->get('request_base_api_log_id'),['url' => $url, 'message'=> $content], 0); // $this->err_code = $error; // $this->err_msg = $this->curl_error[$error]; return false; } }
1、获取pre_auth_code(通过component_access_token)
2、获取component_access_token(通过component_verify_ticek)
3、component_verify_tick(通过三方平台填写后台授权回到地址【10分钟微信推送】)
/** * 获取component_access_token * @return bool|mixed */ public static function getComponentAccessToken() { if (\Yii::$app->cache->exists(self::ComponentAccessToken)) { return \Yii::$app->cache->get(self::ComponentAccessToken); } $app_id = \Yii::$app->params['app_id']; $app_secret = \Yii::$app->params['app_secret']; $component_verify_ticket = self::getComponentVerifyTicket(); $url = 'https://api.weixin.qq.com/cgi-bin/component/api_component_token'; $data = [ 'component_appid' => $app_id, 'component_appsecret' => $app_secret, 'component_verify_ticket' => $component_verify_ticket ]; $json = null; $result = wx_tools::postCurl($url, wx_tools::jsonEncode($data)); if ($result) { $json = wx_tools::parseData($result); if (!$json) { return false; } } $component_access_token = $json['component_access_token']; $expire = $json['expires_in'] ? intval($json['expires_in']) : 7200; \Yii::$app->cache->set(self::ComponentAccessToken, $component_access_token, $expire * 0.9); return $component_access_token; } /** * 获取预授权码 */ public static function getPreAuthCode() { if (\Yii::$app->cache->exists(self::PreAuthCode)) { return \Yii::$app->cache->get(self::PreAuthCode); } $component_access_token = self::getComponentAccessToken(); $app_id = \Yii::$app->params['app_id']; $url = 'https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token=' . $component_access_token; $data = [ 'component_appid' => $app_id ]; $result = wx_tools::postCurl($url, wx_tools::jsonEncode($data)); $json = null; if ($result) { $json = wx_tools::parseData($result); if (!$json) { return false; } $pre_auth_code = $json['pre_auth_code']; $expires_in = $json['expires_in']; \Yii::$app->cache->set(self::PreAuthCode, $pre_auth_code, $expires_in * 0.9); return $pre_auth_code; } return false; }
三方授权事件代码
/** * 三方授权事件接收 */ public function actionNotify() { // file_put_contents('../web/logs/notify/notify'.date('YmdHi').'.txt','微信服务器进来的'); $data = file_get_contents('php://input'); // file_put_contents('../web/logs/notify/data'.date('YmdHi').'.txt',$data); // file_put_contents('../web/logs/notify/get'.date('YmdHi').'.txt',json_encode($_GET)); if ($data) { $time_stamp = empty($_GET['timestamp']) ? "" : trim($_GET['timestamp']); $nonce = empty($_GET['nonce']) ? "" : trim($_GET['nonce']); $msg_sign = empty($_GET['msg_signature']) ? "" : trim($_GET['msg_signature']); if (!$time_stamp || !$nonce || !$msg_sign) { // Log::ERROR('缺少time_stamp,nonce,msg_sign中任一项'); exit; } $app_id = \Yii::$app->params['app_id']; $token = \Yii::$app->params['token']; $encodingAesKey = \Yii::$app->params['encoding_aes_key']; $wx_biz = new WXBizMsgCrypt($token, $encodingAesKey, $app_id); $response = ''; $err_code = $wx_biz->decryptMsg($msg_sign, $time_stamp, $nonce, $data, $response); if ($err_code == 0) { $response = Tools::xmlToArray($response); $info_type = $response['InfoType']; switch ($info_type) { case 'component_verify_ticket': // Log::INFO('create component_verify_ticket'); //保存到缓存 wx_auth::saveComponentVerifyTicket($response['ComponentVerifyTicket']); //TODO break; case 'authorized': //TODO break; case 'unauthorized'; //TODO 取消授权 WxPlatform::updateAuthStatus($response['AuthorizerAppid']); wx_auth::clearAuthorizerAccessToken($response['AuthorizerAppid']); break; case 'updateauthorized': //TODO break; } exit('success'); } else { ApiLog::setMessage(\Yii::$app->session->get(self::RequestBaseApiLogId), $data, 0); // Log::ERROR('解密失败:' . $err_code); exit('fail'); } } ApiLog::setMessage(\Yii::$app->session->get(self::RequestBaseApiLogId), '非微信服务器操作', 0); // Log::ERROR('非微信服务器操作'); exit; }
4、生成页面URL
/** * 公众号授权三方平台页面 * @return string */ public function actionBind() { $params='';//可以带的参数 $redirect_uri = \Yii::$app->request->hostInfo . \Yii::$app->request->scriptUrl . '/public-signal/bind-call-back' . '?params=' . $params; $app_id = \Yii::$app->params['app_id']; $url = "https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid=" . $app_id . "&pre_auth_code=" . $pre_auth_code . "&redirect_uri=" . $redirect_uri; return $this->render('index', [ 'url' => $url ]); }
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>授权页</title>
</head>
<body>
<a href="<?=$url?>" id="authurl" style="display: inline;">
<img src="https://open.weixin.qq.com/zh_CN/htmledition/res/assets/res-design-download/icon_button3_1.png">
</a>
</body>
</html>
/** * 公众号授权三方平台回调地址 完成自己的业务功能 * @return string|void */ public function actionBindCallBack() { $request = \Yii::$app->request; $params = explode(',', $request->get('params')); $auth_code = $request->get('auth_code'); }
男儿立志出乡关 学不成名誓不还