『壹』 怎麼樣使用php的laravel框架快速集成微信登錄
1. 安裝php_weixin_provider
在項目下運行composer require thirdproviders/weixin,即可完成安裝。安裝成功後,在項目的vendor目錄下應該能看到php_weixin_provider的庫文件:
image
2. 配置微信登錄的參數
一共有7個參數可以配置,分別是:
client_id:對應公眾號創建的應用appid
client_secret:對應公眾號創建的應用appid
redirect:對應微信授權成功後的回調地址
proxy_url:對應微信授權的代理服務地址(其作用可閱讀這篇文章了解)
device:區別是PC端微信登錄還是移動端微信登錄,默認值為pc,如果是移動端,可設置為空
state_cookie_name:授權鏈接中會包含隨機的state參數,這個參數在微信回調的時候會原封不動的返回來,屆時可通過驗證state參數是否與授權鏈接中傳入的參數相同,來判斷請求是否有效,防止CSRF攻擊。這個方案,會在授權時把state參數先存到cookie裡面,所以這個參數用來指定這個state參數存入的cookie名稱,默認值是wx_state_cookie
state_cookie_time:指定wx_state_cookie的有效時長,默認是5分鍾
這七個參數有2種設置方式。
第一種是把這些參數以大寫的形式配置到.env配置文件裡面:
image
註:1、每個配置項都是大寫,且以WEIXIN_開頭;2、前三個配置項與前面介紹的參數名稱不完全一致,KEY對應client_id,SECRET對應client_secret,REDIRECT_URI對應redirect;3、其它的與前面介紹的參數名稱一致。
第二種是把這些參數配置到config/services.php文件裡面去:
image
這種方式的配置,每個配置項的名稱與前面介紹的一致。
要注意的內容:
由於php_weixin_provider是基於laravel/socialite實現的,它要求必須配置client_id,client_secret和redirect,否則php_weixin_provider實例化過程就會出錯;對於client_id和client_secret,我認為統一配置在一個地方是沒有問題的,但是對於redirect,如果統一配置,不一定符合所有場景的需求,因為不是每一個用到微信登錄的地方,最後的回調地址都是同一個;所以建議把redirect先配置成一個有效或無效非空的回調地址;反正在後面使用php_weixin_provider的時候還可以在調用的時候更改這個參數的值。
proxy_url如果有,也建議配置在公共的地方;
state_cookie_name和state_cookie_time由於都有默認值,基本上無需重新配置;
device可以在使用的時候再指定。
所有配置參數都可以在使用的時候重新指定。
3. 注冊php_weixin_provider
在項目的config/app.php文件裡面,找到providers配置節,把如下代碼加到它的配置數組裡面:
image
4. 注冊第三方登錄事件的監聽
在項目的app/Providers/EventServiceProvider.php裡面加入以下代碼:
image
laravel框架整體上是一種IOC跟事件驅動的思想,熟悉js就會對事件驅動非常熟悉,熟悉設計模式,就會對IOC(控制反轉,也稱為DI:依賴注入)比較熟悉,這個是理解第3步和第4步配置作用的關鍵。
5. 編寫微信登錄的介面
舉例如下:
+ View code
Socialite::with('weixin')會返回php_weixin_provider的實例,也就是它:
image
拿到這個實例之後,就可以採用鏈式的方式調用它提供的所有public方法,比如設置配置參數,setDevice等等。
6. 編寫微信登錄回調的介面
舉例如下:
+ View code
通過Socialite::with('weixin')拿到php_weixin_provider實例後,調用user方法,就會自動跟微信調用相關介面,並把微信的返回值封裝成對象返回。如果在此過程中,有任何錯誤都會以異常的形式拋出,比如state參數校驗失敗,比如code失效等。
返回的$user對象包含的有效屬性有:
image
小結:
這個方案是基於laravel/socialite實現,並發布到composer來使用的。laravel/socialite是laravel官方提供的第三方登錄的模塊,基於它可以很方便的集成大部分第三方平台的認證,目前它官方已經提供很多第三方的登錄實現:https://socialiteproviders.github.io/。除了國外的facebook,google,github等,國內的微信,微博,qq也都有提供。我在一開始也用的是它官方提供的默認的微信登錄provider來做的,但是後來我發現了以下幾個問題:
1. 不支持微信授權的代理;
2. pc端跟移動端竟然還是分兩個項目來做的:
image
3. 它封裝的user對象里竟然不包含unionid
4. 更改配置參數的方式,實在是讓人覺得難以使用:
image
所以我就在它官方的微信登錄provider基礎上,按照自己的想法,重新實現了一個來解決我發現的這些問題
『貳』 php 怎麼測試微信openid是否成功
//***方法一
獲取code
這里是你的公眾號的APPID&redirect_uri=
用戶點擊確認登錄,自動跳轉下面地址得到code
這個是你自己的跳轉地址
?code=&state=123
後面的這個 ?code=……123 是微信自動跳轉添加的,不是你自己加的
下面是PHP語言,寫在getcode這個頁面里
$code = $_GET['code'];//獲取code
$weixin = file_get_contents("這里是你的APPID&secret=這里是你的SECRET&code=".$code."&grant_type=authorization_code");//通過code換取網頁授權access_token
$jsondecode = json_decode($weixin); //對JSON格式的字元串進行編碼
$array = get_object_vars($jsondecode);//轉換成數組
$openid = $array['openid'];//輸出openid
//***方法二
$appid = "公眾號在微信的appid";
$secret = "公眾號在微信的app secret";
$code = $_GET["code"];
$get_token_url = ''.$appid.'&secret='.$secret.'&code='.$code.'&grant_type=authorization_code';
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$get_token_url);
curl_setopt($ch,CURLOPT_HEADER,0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
$res = curl_exec($ch);
curl_close($ch);
$json_obj = json_decode($res,true);
//根據openid和access_token查詢用戶信息
$access_token = $json_obj['access_token'];
$openid = $json_obj['openid'];
$get_user_info_url = ''.$access_token.'&openid='.$openid.'&lang=zh_CN';
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$get_user_info_url);
curl_setopt($ch,CURLOPT_HEADER,0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
$res = curl_exec($ch);
curl_close($ch);
//解析json
$user_obj = json_decode($res,true);
$_SESSION['user'] = $user_obj;
print_r($user_obj);
『叄』 PHP 微信模擬登錄獲取不到token
已經認證的服務號被檢測到模擬登錄,強制手機(phone=+86158******71 後面的就是被掩碼的手機號)開啟簡訊驗證。只有當真正瀏覽器時就會通過JS觸發簡訊通知,模擬登陸則不會。
在頁面輸入收到的簡訊驗證碼後,才能獲得 登錄token。
這是微信官方的技術手段,此問題暫時無解。
『肆』 PHP開發微信授權登錄教程
微信授權登錄方式採用Oauth2.0鑒權,分為靜默授權與彈窗授權。靜默授權下,用戶無需手動同意即可直接進入回調頁面,僅獲取用戶的openid。彈窗授權則需用戶手動同意,用於獲取用戶的基本信息。
兩種scope的區別在於,snsapi_base為scope的授權僅用於獲取用戶的openid,過程為靜默授權並自動跳轉至回調頁面。snsapi_userinfo為scope的授權需用戶手動同意,一旦同意便無需再次關注,即可在授權後獲取該用戶的基本信息。
獲取用戶基本信息需在用戶與公眾號產生消息交互或關注後事件推送後,通過用戶OpenID來調用介面實現。具體授權流程包含四步:引導用戶進入授權頁面同意授權,獲取code;通過code換取網頁授權access_token;開發者可刷新網頁授權access_token,避免過期;通過網頁授權access_token和openid獲取用戶基本信息。
微信操作類需封裝兩個數據表,用於保存access_token、ticket,考慮到它們的有效期限及每日請求數上限,開發者需自行保存。以下為具體代碼示例。
業務代碼實現方式需根據具體需求調整,確保授權流程的正確性和用戶信息的獲取與保護。開發者應熟悉微信介面文檔,確保代碼的正確性和安全性。
『伍』 php微站微信公眾號授權登錄
php微站微信公眾號授權登錄,即在微信客戶端的瀏覽器裡面授權登錄
1.getBaseInfo.php文件獲取code ?php //1.獲取到code $appid = "***************"; $redirect_uri = urlencode("http://**********.com/weixin_wap_shouquan/getWxCode.php"); $url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=".$appid."redirect_uri=".$redirect_uri."response_type=codescope=snsapi_userinfostate=0#wechat_redirect"; header("Location:".$url); ?
2.getWxCode.php文件獲取微信用戶信息並存入資料庫 ?php require_once './httpCurl.php'; require_once './MySQLiDb.class.php'; //2.獲取到網頁授權的access_token $appid = "******************"; $app_secret = "************************"; $code = isset($_GET["code"])? trim($_GET["code"]) : ""; $url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=".$appid."secret=".$app_secret."code=".$code."grant_type=authorization_code"; /*$res = object(stdClass)#1 (6) { ["access_token"]= string(107) "***************************" ["expires_in"]= int(7200) ["refresh_token"]= string(107) "****************************" ["openid"]= string(28) "***********************" ["scope"]= string(12) "snsapi_login" ["unionid"]= string(28) "**************************" }*/ //3.拉取用戶的openid $res = json_decode(httpCurl($url)); $access_token = $res-access_token; $openid = $res-openid; //4獲取用戶信息 $userUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=".$access_token."openid=".$openid; /*$userInfo = object(stdClass)#2 (10) { ["openid"]= string(28) "***********************" ["nickname"]= string(6) "***" ["sex"]= int(1) ["language"]= string(5) "zh_CN" ["city"]= string(7) "Baoding" ["province"]= string(5) "Hebei" ["country"]= string(2) "CN" ["headimgurl"]= string(140) "*******************************" ["privilege"]= array(0) { } ["unionid"]= string(28) "*************************" }*/ $userInfo = json_decode(httpCurl($userUrl)); //獲取微信unionid $wx_unionid = $userInfo-unionid; $info = $db-getRow("SELECT * FROM user WHERE wx_unionid='$wx_unionid' LIMIT 1"); if (!!$info){ //跳轉到登錄頁面 header("Location:/m/login_ajax.php?t=0wx_unionid=".$wx_unionid); }else{ //將微信用戶信息存入資料庫 $user_sn = randomString().$db-autoId("user"); $res = $db-query("INSERT INTO user (user_sn,auth_type,wx_unionid,wx_nickname,wx_avatar,wx_sex,add_time,wx_open_id) VALUES('$user_sn','0','$wx_unionid','$userInfo-nickname','$userInfo-headimgurl','$wx_sex',now(),'$openid')"); if (!!$res){ header("Location:/m/login_ajax.php?t=0wx_unionid=".$wx_unionid); }else{ header("Location:/m/login.php"); } } /* * 獲取隨機串 * */ function randomString($type=1,$length=4){ if ($type == 1){ $chars = join("",range(0,9)); }elseif ($type == 2){ $chars = join("",array_merge(range("a","z"),range("A","Z"))); }elseif ($type == 3){ $chars = join("",array_merge(range("a","z"),range("A","Z"),range(0,9))); } if ($length strlen($chars)){ exit("字元串長度不夠"); } $chars = str_shuffle($chars); return substr($chars,0,$length); } ?
3.httpCurl.php文件 ?php function httpCurl($url, $method="", $postfields = null, $headers = array(), $debug = false) { $ci = curl_init(); /* Curl settings */ curl_setopt($ci, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, 30); curl_setopt($ci, CURLOPT_TIMEOUT, 30); curl_setopt($ci, CURLOPT_RETURNTRANSFER, true); switch ($method) { case 'POST': curl_setopt($ci, CURLOPT_POST, true); if (!empty($postfields)) { curl_setopt($ci, CURLOPT_POSTFIELDS, $postfields); } break; } if ( strlen( $url ) 5 strtolower( substr( $url , 0 , 5 ) ) == 'https' ){ curl_setopt( $ci , CURLOPT_SSL_VERIFYPEER , FALSE ); curl_setopt( $ci , CURLOPT_SSL_VERIFYHOST , FALSE ); } curl_setopt($ci, CURLOPT_URL, $url); curl_setopt($ci, CURLOPT_HTTPHEADER, $headers); curl_setopt($ci, CURLINFO_HEADER_OUT, true); $response = curl_exec($ci); // $http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE); $http_code = curl_getinfo($ci); if ($debug) { echo "=====post data======\r\n"; var_mp($postfields); echo '=====info=====' . "\r\n"; print_r(curl_getinfo($ci)); echo '=====$response=====' . "\r\n"; print_r($response); } curl_close($ci); return $response; } ?
4.MySQLiDb.class.php文件 ?php //連接資料庫 class MySQLiDb{ private $dbhost; // 資料庫主機 private $dbuser; // 資料庫用戶名 private $dbpass; // 資料庫用戶名密碼 private $dbname; // 資料庫名 private $link; //資料庫鏈接本身 private $charset; // 資料庫編碼,GBK,UTF8,gb2312 private $sql;//最後一次執行的sql語句 //初始化類 public function __construct($dbhost, $dbuser, $dbpass, $dbname = '', $charset = 'utf8'){ $this-dbhost = $dbhost; $this-dbuser = $dbuser; $this-dbpass = $dbpass; $this-dbname = $dbname; $this-charset = strtolower(str_replace('-', '', $charset)); $this-link = new mysqli($this-dbhost,$this-dbuser,$this-dbpass,$this-dbname); if($this-error()){ $errorInfo='Error:Could not make a database link ('.$this-errno().')'.$this-error(); throw new ErrorException($errorInfo, 1); } $this-setCharset($this-charset); } /** * @param 設置字元集 */ private function setCharset($charset){ $this-link-set_charset($charset); } /** * 資料庫執行語句,可執行查詢添加修改刪除等任何sql語句 * @access public * @param $sql string 查詢sql語句 * @return $result,成功返回資源,失敗則輸出錯誤信息,並退出 */ public function query($sql){ $this-sql = $sql; $result = $this-link-query($this-sql); if (!$result) { return false; // die($this-errno().':'.$this-error().'br /出錯語句為'.$this-sql.'br /'); } return $result; } /** * 拼裝查詢的sql語句 * @param $tablename 數據表名稱 * @param $opts 查詢的元素 * @param $where 條件 數組或字元串 * @param $order 排序條件 * @return 返回sql語句 */ public function selectSql($tablename,$opts=array(),$where=" 1=1 ",$order=" id DESC "){ if (empty($opts)){ $fields = "*"; }else{ $opts = $this-cleanseArray($opts); $keys = array_keys($opts); $vals = array_values($opts); $fields = ""; for ($i = 0; $i count($keys); $i++) { if ($fields == ""){ $fields = $vals[$i]; } else { $fields = $fields.",".$vals[$i].""; } } } if (is_array($where)){ $whereArr = $this-cleanseArray($where); $keysWhere = array_keys($whereArr); $valsWhere = array_values($whereArr); $whereString = ""; for ($j = 0; $j count($keysWhere); $j++) { if ($whereString == ""){ $whereString = $keysWhere[$j]." = '".$valsWhere[$j]."'"; } else { $whereString = $whereString." AND ".$keysWhere[$j]." = '".$valsWhere[$j]."'"; } } }else{ $whereString = $where; } $sql = "SELECT ".$fields." FROM ".$tablename." WHERE ".$whereString." ORDER BY ".$order; return $sql; } /** * 更新的通用語句 * @param $tablename 數據表名稱 * @param $opts 更新的元素 * @param $where 條件 數組或字元串 * @return 影響的行數 */ public function genericUpdate($tablename, $opts, $where) { $opts = $this-cleanseArray($opts); $keys = array_keys($opts); $vals = array_values($opts); $setString = ""; $whereString = ""; for ($i = 0; $i count($keys); $i++) { if ($setString == ""){ $setString = $keys[$i]." = '".$vals[$i]."'"; } else { $setString = $setString.", ".$keys[$i]." = '".$vals[$i]."'"; } } if (is_array($where)){ $whereArr = $this-cleanseArray($where); $keysWhere = array_keys($whereArr); $valsWhere = array_values($whereArr); for ($j = 0; $j count($keysWhere); $j++) { if ($whereString == ""){ $whereString = $keysWhere[$j]." = '".$valsWhere[$j]."'"; } else { $whereString = $whereString." AND ".$keysWhere[$j]." = '".$valsWhere[$j]."'"; } } }else{ $whereString = $where; } $sql = "UPDATE `$tablename` SET ".$setString." WHERE ".$whereString; $result = $this-query($sql); if (!!$result){ return $this-affectedRows(); }else{ return false; } } /** * 刪除的通用語句 * @param $tablename 數據表名稱 * @param $where 條件 數組或字元串 * @return 影響的行數 */ function genericDelete($tablename, $where="1") { $whereString = ""; if (is_array($where)){ $whereArr = $this-cleanseArray($where); $keysWhere = array_keys($whereArr); $valsWhere = array_values($whereArr); for ($j = 0; $j count($keysWhere); $j++) { if ($whereString == ""){ $whereString = $keysWhere[$j]." = '".$valsWhere[$j]."'"; } else { $whereString = $whereString." AND ".$keysWhere[$j]." = '".$valsWhere[$j]."'"; } } }else{ $whereString = $where; } $sql = "DELETE FROM ". $tablename . " WHERE " . $whereString; $result = $this-query($sql); if (!!$result){ return $this-affectedRows(); }else{ return false; } } /** * 過濾字元串 * @param 字元串 * @param string $char * @return $cleansed */ private function cleansePureString($str, $char = '\\') { ///[^a-zA-Z0-9\s]/ NON ALPHA $cleansed = preg_replace("/[%_'\"]/", '', $str); //_, % and ' return $cleansed; } /** * 過濾數組 * @param unknown $opts * @return multitype:$cleansed */ private function cleanseArray($opts) { $cleansedArray = array(); $keys = array_keys($opts); $vals = array_values($opts); for ($i = 0; $i count($keys); $i++) { $newKey = $this-cleansePureString($keys[$i],"'"); $newVal = $this-cleansePureString($vals[$i],"'"); $cleansedArray[$newKey] = $newVal; } return $cleansedArray; } /** * return 返回一個字元串指出了客戶端庫的版本 */ public function clientInfo(){ return $this-link-client_info; } /** * return 作為一個整數返回MySQL伺服器的版本 */ public function clientVersion(){ return $this-link-client_version; } /** * 獲取結果集中行的數目 * @access private * @return 結果集中行的數目 */ function numRows($sql) { $result = $this-query($sql); if (!!$result){ $nums = $result-num_rows; return $nums; }else{ return false; } } /** * 獲取第一條記錄的第一個欄位 * @access public * @param $sql string 查詢的sql語句 * @return 返回一個該欄位的值 */ public function getOne($sql){ $result = $this-query($sql); if (!!$result) { $row = $result-fetch_row(); return $row[0]; } else { return false; } } /** * 獲取一條記錄 * @access public * @param $sql 查詢的sql語句 * @return array 關聯數組 */ public function getRow($sql){ $result = $this-query($sql); if ($result) { $row = $result-fetch_assoc(); return $row; } else { return false; } } /** * 獲取所有的記錄 * @access public * @param $sql 執行的sql語句 * @return $list 返回所有記錄組成的二維數組 */ public function getAll($sql){ $result = $this-query($sql); $list = array(); if (!!$result){ while (!!($row = $result-fetch_assoc())){ $list[] = $row; } return $list; }else{ return false; } } /** * 獲取某一列的值 * @access public * @param $sql string 執行的sql語句 * @return $list array 返回由該列的值構成的一維數組 */ public function getCol($sql){ $result = $this-query($sql); $list = array(); if (!!$result){ while (!!($row = $result-fetch_row())) { $list[] = $row[0]; } return $list; }else{ return false; } } /** * 獲取上一步insert操作產生的id */ public function getInsertId(){ return $this-link-insert_id; } /** * 獲取下一個自增(id)值 * @param $tablename 數據表名 */ public function autoId($tablename) { return $this-getOne("SELECT auto_increment FROM information_schema.`TABLES` WHERE TABLE_SCHEMA='" . $this-dbname . "' AND TABLE_NAME = '" . trim($tablename, '`') . "'"); } /** * 獲取影響的記錄行數 * @access private * @return 前一次 MySQL 操作所影響的記錄行數 */ public function affectedRows() { return $this-link-affected_rows; } /** * 返回結果集中欄位的數 * @access public * @param $sql string 執行的sql語句 * @return 結果集中欄位的數 */ public function numFields($sql) { $result = $this-query($sql); return $result-field_count; } /** * 釋放結果內存 * @access public * @param $result 要釋放的結果標識符 * @return 釋放結果內存 */ public function freeResult($result) { return $result-free(); } /** * 關閉先前打開的資料庫連接 */ public function close(){ $this-link-close(); } /** * 獲取錯誤號 * @access private * @return 錯誤號 */ private function errno(){ return $this-link-connect_errno; } /** * 獲取錯誤信息 * @access private * @return 錯誤private信息 */ private function error(){ return $this-link-connect_error; } //開啟事務 public function startTransaction(){ $this-link-autocommit(FALSE); } //提交事務 public function commit(){ $this-link-commit(); $this-link-autocommit(TRUE); } //回滾事務 public function rollback(){ $this-link-rollback(); } } header("Content-type:text/html; charset=UTF-8"); // database host $dbhost = "localhost"; // database name $dbname = "***"; // database username $dbuser = "***"; // database password $dbpass = "****"; // 字元集 $charset = 'utf8'; $db = new MySQLiDb($dbhost, $dbuser, $dbpass, $dbname, $charset); ?
5.目錄結構