幾年前,為了接入微信,用C#研究了一個禮拜才做出個對接元件。在 Laravel 使用 Socialite 兩天搞定,喜出望外的是用這種方式貌似可以對接所有的OAuth認證系統,真是事半功倍。關於 Laravel Socialite 中文的英文的自己去找。我寫我自己感覺需要注意的地方。
我的目標是公眾號的頁面分為兩種,一種是隨便可以訪問的,不需要認證,甚至可以在瀏覽器裡檢視,另一種是隻有通過微信認證授權才能訪問的頁面。在C#中我做了兩個Page的抽象類,一個是普通的頁面,另一個是發現還沒有 openid 就跳轉到認證伺服器兌換到 openid 後再繼續的頁面。本著這個目標繼續折騰。
走過的彎路沒有參考價值就不多寫了。下邊就把 Evernote 中的內容貼出來。
參考的網址有:
https://www.open-open.com/lib/view/open147...
https://learnku.com/docs/laravel/5.7/socialite
https://github.com/SocialiteProviders/Prov...
https://socialiteproviders.netlify.com
https://socialiteproviders.netlify.com/pro...
//先安裝了
composer require laravel/socialite
//又安裝了(後來感覺只安裝這個就行了,上邊那個應該不需要,似乎這個裡已經包含了上一個)
composer require socialiteproviders/weixin
//然後按照 https://socialiteproviders.netlify.com/pro... 中的說明修改程式碼
第一處修改參照:2. Service Provider
第二處修改參照:3. Event Listener
第三處修改參照:4. Configuration setup
修改到這裡就開始 5. Usage,哥感動地都快哭了
再 參照 https://learnku.com/docs/laravel/5.7/socialite 寫了路由
Route::get('login/github', 'Auth\LoginController@redirectToProvider');
Route::get('login/github/callback', 'Auth\LoginController@handleProviderCallback');
在 redirectToProvider 寫了:
return Socialite::with('Weixin')->redirect();
如果在電腦瀏覽器中訪問,出現了熟悉的綠色提示在微信中訪問
有點小激動
馬上公眾號註冊選單試著方法在方法 handleProviderCallback 等待 $user 中的資料
handleProviderCallback中是這麼寫的:
public function handleProviderCallback()
{
$user = Socialite::driver('github')->user();
// $user->token;
}
手機訪問報錯(如果不報錯,就見鬼了)
錯誤提示:The bootstrap/cache directory must be present and writable.
執行 php artisan cache:clear 清緩問題解決
接著第二個錯 InvalidStateException
或曰:修改程式碼如下:
$user = Socialite::driver('weixin')->stateless()->user();
接著報第三個錯:cURL error 60: SSL certificate problem: unable to get local issuer certificate
到這裡心有點發涼了,這尼瑪超扯越遠,繼續折騰才三個錯了
從 https://curl.haxx.se/ca/cacert.pem 下載最新的cacert.pem
修改 PHP.ini 檔案
curl.cainfo=C:\Program Files\PHP\7.2.13\cacert.pem
或曰:這個證照不行需要下載另一個
再一試好了。三個錯就通了。 $user 中取到了個人微信的資訊,我渴望的openid也在其中。
後來發現的:只要有錯誤都會報 InvalidStateException 這個錯誤,其實第二個錯是由第三個錯引起來的,因為無意中刪除掉 ->stateless 也不會報 InvalidStateException。還是我自己的程式碼寫錯也會報 InvalidStateException。
心情是喜悅的,但不對呀我的目標還沒達到。
這種機制為什麼 redirect 的 Callback 是個固定的地址呢,我是希望這麼寫
return Socialite::driver('weixin')->redirect(<這裡輸入一個地址,就是授權後回的地址>);
我用C#編寫的元件就是可以指定的。這樣可以實現:訪問一個授權頁面發現沒有授權然後去授權,授權後再回到這個頁面。
任重道遠繼續折騰,這是我最怕的:我的需求不在人家的考慮範圍之內,甚至與人家的理念違背。
經過折騰無意發現在 handleProviderCallback 中通過如下程式碼可取到授權以前的地址。
$previous = $session->get('_previous')['url'];
程式碼雖簡單,由於本人PHP還在入門水平折騰了快一天,翻看Socialite的原始碼,還是無意中發現的
大家熟悉的PHP函式我需要查百度才知道,大家別笑哈。
最後我的實現方法是
建立一個 BaseController 頁面的 Controller 從這個類繼承,如果需要授權保護則繼承它並 呼叫 redirectToProvider
/**
* 需要使用者 OPENID 的控制器的基類
*
* Class BaseController
* @package App\Http\Controllers\WeiXin
*/
class BaseController extends Controller
{
/**
* 重定向到認證伺服器
*
* @param Request $request
* @param \Closure $getView
* @return mixed
*/
protected function redirectToProvider(Request $request, \Closure $getView)
{
$session = $request->session();
$openid = $session->get('openid');
if ($openid) {
Log::info("網頁取到OPENID:{$openid},顯示當前頁面內容");
return $getView();
} else {
$session->flush();
Log::info("沒有取到OPENID,重新定位到微信認證伺服器");
return Socialite::driver('weixin')->redirect();
}
}
}
子類是這樣的:
class HomeController extends BaseController
{
public function home(Request $request){
return $this->redirectToProvider($request, function (){
return view('child', ['name' => '這是路由器裡定義的變數']);
});
}
}
負責捕獲回撥的單獨在一個Controller
/**
*
* @param Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function handleProviderCallback(Request $request)
{
$user = Socialite::driver('weixin')->user();
if ($user) Log::info("已經取到使用者資訊");
//TODO:儲存使用者資訊到資料庫中
//設定全域性 openid 到 session 中
$openid = $user->id;
if ($openid) Log::info("已經取到使用者OPENID:$openid");
$session = $request->session();
$session->put('openid', $openid);
//定位到之前的網頁
$previous = $session->get('_previous')['url'];
Log::info("定位到原來的地址:$previous");
return redirect()->away($previous);
}
到這裡就結束了,目前看上去沒有問題。用著再說吧。
上次一個朋友批評了我不分享的行為。這此把我的感覺寫出來,希望對另人有一點幫助。
由於本人PHP小白一個,有什麼更好的方法,望不吝賜教!!!