php-基于laravel框架实现微信公众号扫码关注登录
后台-插件-广告管理-内容页头部广告(手机) |
一、依赖包
1. 关于实现思路流程:可参考以下大佬文章:
laravel结合esaywechat组件实现微信授权登录
Laravel框架 微信授权登陆 代码封装
Laravel——微信授权登陆
1.1 php依赖包网站上输入overtrue/wechat
1.2 进入文档官网
1.3 本文是基于php8.0.2版本的,laravel框架8.5版本,至于为何使用8.0.2版本,是由于在实现支付宝支付功能时,低版本php均会出现composer安装依赖问题,easywechat本文安装的是5.x版本。安装easywechat依赖:(在自己项目目录下运行即可)
composer require overtrue/wechat:~5.0 -vvv1.4 创建配置文件
php artisan vendor:publish --provider="Overtrue\\LaravelWeChat\\ServiceProvider"二、配置项目config
2.1 在config中创建easywechat.php(名字可自己定义):
- <?php
- return [
- 'easyWechat' => [
- /**
- * 账号基本信息,请从微信公众平台/开放平台获取
- */
- 'app_id' => '', // AppID
- 'secret' => '', // AppSecret
- 'token' => '', // Token
- 'aes_key' => '', // EncodingAESKey,兼容与安全模式下请一定要填写!!!
- /**
- * 指定 API 调用返回结果的类型:array(default)/collection/object/raw/自定义类名
- * 使用自定义类名时,构造函数将会接收一个 `EasyWeChat\Kernel\Http\Response` 实例
- */
- 'response_type' => 'array',
- ],
- /**
- * 日志配置
- *
- * level: 日志级别, 可选为:
- * debug/info/notice/warning/error/critical/alert/emergency
- * path:日志文件位置(绝对路径!!!),要求可写权限
- */
- 'log' => [
- 'default' => 'dev', // 默认使用的 channel,生产环境可以改为下面的 prod
- 'channels' => [
- // 测试环境
- 'dev' => [
- 'driver' => 'single',
- 'path' => storage_path('logs/easywechat.log'),
- 'level' => 'debug',
- ],
- // 生产环境
- 'prod' => [
- 'driver' => 'daily',
- 'path' => storage_path('logs/easywechat.log'),
- 'level' => 'info',
- ],
- ],
- ],
- /**
- * 接口请求相关配置,超时时间等,具体可用参数请参考:
- * http://docs.guzzlephp.org/en/stable/request-config.html
- *
- * - retries: 重试次数,默认 1,指定当 http 请求失败时重试的次数。
- * - retry_delay: 重试延迟间隔(单位:ms),默认 500
- * - log_template: 指定 HTTP 日志模板,请参考:https://github.com/guzzle/guzzle/blob/master/src/MessageFormatter.php
- */
- 'http' => [
- 'max_retries' => 1,
- 'retry_delay' => 500,
- 'timeout' => 5.0,
- // 'base_uri' => 'https://api.weixin.qq.com/', // 如果你在国外想要覆盖默认的 url 的时候才使用,根据不同的模块配置不同的 uri
- ],
- /**
- * OAuth 配置
- *
- * scopes:公众平台(snsapi_userinfo / snsapi_base),开放平台:snsapi_login
- * callback:OAuth授权完成后的回调页地址
- */
- 'oauth' => [
- 'scopes' => ['snsapi_userinfo'],
- 'callback' => '/examples/oauth_callback.php',
- ],
- ];
2.2 关于微信公众号配置信息数据,刚开始可以用官方文档的接口测试号,直接申请即可,用于初步调试方便,后面调试通了,再用实际微信公众号的配置信息数据(需要申请),如何申请,直接百度即可。
接口测试号申请:
微信官方文档-公众号
测试号管理:
注意点:在提交以上的【接口配置信息】时,填入的URL,需要返回echostr,才能提交配置信息成功,因为微信会向该接口发送请求,成功访问并且收到echostr返回,微信方才能确定该接口成功,3.1处有说明。微信官方文档里也写有:
三、微信公众号服务器验证
3.1 配置好config,以及测试号管理之后,按照所填入的url,创建该接口:
- /**
- * 微信公众号服务器验证
- */
- public function notifyEasyWeChat(Request $request)
- {
- $data = $request->all();
- $boolean = self::checkSignature($data);
- Log::info('easywechat', $data);
- Log::info('checkSignature', [$boolean]);
- return $data['echostr'];
- }
- /**
- * 验证
- */
- function checkSignature($data)
- {
- $signature = $data["signature"];
- $timestamp = $data["timestamp"];
- $nonce = $data["nonce"];
- $token = config('xxx.xxx.token');//config里自己定义的token,
- $tmpArr = array($token, $timestamp, $nonce);
- sort($tmpArr, SORT_STRING);
- $tmpStr = implode($tmpArr);
- $tmpStr = sha1($tmpStr);
- if ($tmpStr == $signature) {
- return true;
- } else {
- return false;
- }
- }
此处该接口必须定义为get请求,然后返回微信请求的echostr,方能验证接口成功,提交成功2.2处的接口配置信息。可以将其easywechat通过log日志打印出来,如下:
- easywechat
- {
- "signature":"f56fcdd5053370b7a3803777856a08e87dade380",
- "echostr":"8190732441967026149",
- "timestamp":"1704330081",
- "nonce":"784151826"
- }
- [2024-01-04 09:30:32] production.INFO: checkSignature [true]
四、微信公众号配置信息
4.1 开通后,然后生成的微信公众号配置信息如下:
4.2 上面的2、3、4、5、6、7都是必须的,测试阶段的话,上面的7,可以用明文与密文模式,方便调试,上面的3,必须把自己的服务器的ip填入,不然后续获取access_token,会报错。其他的直接按照要求生成,填入config即可,上面的6对应config的aes_key,上面的2对应的是secret。本人做的时候,都是后知后觉的,走了不少弯路。
五、生成微信二维码
5.1 书写生成微信登录二维码接口:(完成四之后)
- <?php
- namespace App\Http\Controllers\Auth;
- use App\Http\Controllers\BaseController;
- use App\Http\Response\ApiResponse;
- use EasyWeChat\Factory;
- use Illuminate\Support\Facades\Cache;
- class WeChatController extends BaseController
- {
- /**
- * 生成微信登录二维码
- */
- public function qrcodeCreate()
- {
- $config = config('easywechat.easyWechat');
- $app = Factory::officialAccount($config);
- $uuid = date('YmdHis') . rand(0000, 9999);
- $scene_value = 'login-' . $uuid;//此处通过二维码带参数一起带过去,可自己定义
- Cache::put($uuid, 1, 5 * 3600);//此处缓存,可去掉,本人自己的代码逻辑
- $result = $app->qrcode->temporary($scene_value, 5 * 3600);
- $url = $app->qrcode->url($result['ticket']);
- $data = [
- 'qrcpde_url' => $url,
- 'uuid' => $uuid,
- ];
- return ApiResponse::successData($data);
- }
- }
上述代码,使用的是easywechat第三方包依赖实现的,可以在easywechat文档中详见:
接口返回数据:
六、接收微信扫码点击关注公众号之后响应数据(解密)
6.1 定义路由,请求为post,url是第三点验证路由一样,差别在于一个get请求,一个post请求。
- /**
- * 微信公众号
- */
- //微信公众号服务器验证
- $api->get('wechat/easyNotify', [NotifyController::class, 'notifyEasyWechat']);
- //微信公众号扫码登录回调
- $api->post('wechat/easyNotify', [NotifyController::class, 'notifyEWXML']);
6.2 解密用得是openssl,由于微信官方文档提供过于复杂,以及在php7.2之后,一些函数已经是弃用了,也可以说,对于php8.0.2来说,官方提供的解密源代码已经过期了,以下是我参考:微信公众号之安全模式下消息加解密
本人非常感谢这位老哥的文档,解密中的OPENSSL_ZERO_PADDING解决了我一天半都解决不了的bug,在于解密未关注与关注的密文中,关注的密文解不了的问题。以下是本人的代码:
- /**
- * 微信公众号扫码登录回调
- */
- public function notifyEWXML()
- {
- // 获取微信服务器推送的数据
- $postData = file_get_contents("php://input");
- //解密与解析xml
- $data = self::xmlToArray($postData);
- //业务操作
- if ($data['Event'] === 'subscribe' || $data['Event'] === 'SCAN') {
- $open_id = $data['FromUserName'];
- $uuid = explode('-', $data['EventKey']);
- $cache_uuid = 'login-' . $uuid[1];
- Cache::put($cache_uuid, $open_id, 5 * 60);
- $user = User::where('open_id', $open_id)->first();
- if (empty($user)) {
- $new_user = new User();
- $new_user->open_id = $open_id;
- $new_user->nickname = '微信用户';
- $new_user->save();
- }
- //响应数据
- event(new WeChatSubscribeEvent($open_id));//此处,是后续的功能,回复用户:登录成功
- }
- echo '';
- }
- /**
- * 使用 simplexml_load_string 解析 XML 数据
- * @param $postData --微信公众号响应的xml数据
- */
- public static function xmlToArray($postData)
- {
- //将xml进行解析为数组格式
- $orginArray = json_decode(json_encode(simplexml_load_string($postData, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
- Log::info('源数据:', $orginArray);
- //获取微信公众号提供的消息加解密密钥
- $encodingAESKey = config('easywechat.easyWechat.aes_key');
- //由于微信公众号提供的43位,需要加一位,为4的倍数,微信方要求的
- $encodingAESKey = base64_decode($encodingAESKey . '=');
- //openssl_decrypt进行解密
- $decryptedData = self::decryptWeChatResponse($orginArray['Encrypt'], $encodingAESKey);
- //去掉额外的没用的字符串
- $pattern = '/
(.*?)<\/xml>/s' ; - $postArray = '';
- if (preg_match($pattern, $decryptedData, $matches)) {
- $xmlData = $matches[0];
- //xml转数组
- $postArray = json_decode(json_encode(simplexml_load_string($xmlData, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
- }
- Log::info('解析xml后数据', [$postArray]);
- return $postArray;
- }
- /**
- * 微信公众号解密
- * @param $encryptedData --加密的数据
- * @param $encodingAESKey --微信公众号上的消息加解密密钥
- */
- public static function decryptWeChatResponse($encryptedData, $encodingAESKey)
- {
- // 将 Base64 编码的加密数据解码
- $decodedData = base64_decode($encryptedData);
- // 获取 AES 密钥的前 32 个字符作为实际密钥
- $encodingAESKey = substr($encodingAESKey, 0, 32);
- // 使用 openssl_decrypt 进行解密
- $decryptedData = openssl_decrypt(
- $decodedData, // 待解密的数据
- 'aes-256-cbc', // 解密算法
- $encodingAESKey, // 解密密钥
- OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, // 输出原始数据,不进行 base64 编码,此处很关键
- substr($encodingAESKey, 0, 16) // 初始化向量(IV),长度为 16 字节
- );
- Log::info('解密后数据', [$decryptedData]);
- // 返回解密后的数据
- return $decryptedData;
- }
6.3 解密关注响应的密文的关键:
6.4 解密后的格式如下:
EventKey中,正是携带的参数
6.5 如果在实现中碰到的问题,需要往composer.json添加的,直接添加即可,其他问题,需要自己解决了。本文,是本人实现后所记录的,其中很多细节无法复现了,也碰到了不少的问题。不过本人都能解决,各位大佬肯定也能。
七、响应用户消息
7.1 功能实现效果如下:
7.2 创建事件处理程序:
php artisan make:event WeChatSubscribeEvent7.3 在生成的 WeChatSubscribeEvent 类中定义处理逻辑,例如:
- namespace App\Events;
- use Illuminate\Foundation\Events\Dispatchable;
- class WeChatSubscribeEvent
- {
- use Dispatchable;
- public $openid;
- public function __construct($openid)
- {
- $this->openid = $openid;
- }
- }
7.4 注册事件监听器:
在 EventServiceProvider 中注册事件监听器:
- // app/Providers/EventServiceProvider.php
- protected $listen = [
- 'App\Events\WeChatSubscribeEvent' => [
- 'App\Listeners\WeChatSubscribeListener',
- ],
- ];
7.5 创建事件监听器:
创建一个事件监听器,用于处理关注事件:
php artisan make:listener WeChatSubscribeListener在生成的 WeChatSubscribeListener 类中定义响应逻辑,例如:
- // app/Listeners/WeChatSubscribeListener.php
- namespace App\Listeners;
- use App\Events\WeChatSubscribeEvent;
- class WeChatSubscribeListener
- {
- public function handle(WeChatSubscribeEvent $event)
- {
- $openid = $event->openid;
- // 在这里编写响应逻辑,例如发送“登录成功”的消息
- // 使用 EasyWeChat 的 OfficialAccount 实例来发送消息
- // 在这里编写响应逻辑,例如发送“登录成功”的消息
- $openid = $event->openid;
- $config = config('xxx.xxx');
- $app = Factory::officialAccount($config);
- $app->customer_service->message('登录成功!')->to($openid)->send();
- }
- }
7.6 触发事件:
- // 在你的控制器或其他地方触发关注事件
- event(new \App\Events\WeChatSubscribeEvent($openid));
放到微信响应的代码中即可。
八、总结
这是本人第一次写文章,比较生疏,后续还会有更新。本人刚从事php方向一年,之前用的框架是tp6,第一次接触laravel框架,又爱又恨,恨其依赖问题过于繁琐,麻烦,难以解决,不过,这也是没办法避免的。希望大家点点关注,给予小白一些写作动力,能关注一下php小白的平凡之旅!非常感谢,大家有问题可以私信,看到会积极响应的。
1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。
在线投稿:投稿 站长QQ:1888636
后台-插件-广告管理-内容页尾部广告(手机) |