Laravel Socialite 对接微信公众号成功实践

几年前,为了接入微信,用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小白一个,有什么更好的方法,望不吝赐教!!!

本作品采用《CC 协议》,转载必须注明作者和本文链接
一亩三分地儿
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!