流程
- 访问 PC 网页,通过微信接口生成一个临时二维码,二维码夹带一个 token 参数进行识别。
- PC 网页向服务端进行轮询
- 手机微信扫描二维码进入(关注)公众号
- 微信推送信息到服务端
- 服务端接收到微信推送后进行处理
- PC 网页收到服务端成功信息后停止轮询
依赖
1
| $ composer require overtrue/wechat:~4.0
|
数据表
需要创建两个表,一个保存用户信息,一个保存 token 信息。如果应用到项目上需要自行调整。
database\migrations\2020_01_01_000000_create_users_table.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| <?php
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema;
class CreateUsersTable extends Migration { protected $table = 'users';
public function up() { if(Schema::hasTable($this->table)) { return true; }
Schema::create($this->table, function (Blueprint $table) { $table->id(); $table->string('openid', 30)->unique()->comment('openid'); $table->string('nickname', 100)->comment('昵称'); $table->timestamps(); });
\DB::statement("ALTER TABLE `$this->table` comment '用户表'"); }
public function down() { Schema::dropIfExists($this->table); } }
|
database\migrations\2020_01_01_000000_create_token_table.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| <?php
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema;
class CreateTokenTable extends Migration { protected $table = 'token';
public function up() { if(Schema::hasTable($this->table)) { return true; }
Schema::create($this->table, function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('uid')->nullable()->comment('UID'); $table->string('value', 40)->unique()->comment('令牌值'); $table->unsignedTinyInteger('status')->default(0)->comment('状态 0 未使用 1 已使用'); $table->timestamps(); });
\DB::statement("ALTER TABLE `$this->table` comment '令牌表'"); }
public function down() { Schema::dropIfExists($this->table); } }
|
运行迁移。
模型
app\Models\Users.php
1 2 3 4 5 6 7 8 9 10
| <?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Users extends Model { protected $table = 'users'; }
|
app\Models\Token.php
1 2 3 4 5 6 7 8 9 10
| <?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Token extends Model { protected $table = 'token'; }
|
控制器
IndexController
控制器用于视图显示和轮询处理。
WechatController
控制器用于接收微信推送。
app\Http\Controllers\IndexController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| <?php
namespace App\Http\Controllers;
use App\Models\Users; use App\Models\Token; use EasyWeChat\Factory; use Illuminate\Support\Str; use Illuminate\Http\Request;
class IndexController extends Controller { public function index(Request $request) { $config = [ 'app_id' => 'wx120dfab834000d0c', 'secret' => '2126be89ea53de4976c4cebdac79a74f', ];
$app = Factory::officialAccount($config);
if($request->isMethod('GET')) { $token = new Token; $token->value = Str::random(40);
if(!$token->save()) { return '创建 token 失败'; }
$qrcode = $app->qrcode->temporary($token->value, 5 * 60);
$image_url = $app->qrcode->url($qrcode['ticket']);
return view('welcome', [ 'token' => $token->value, 'image_url' => $image_url, ]); }
$token_str = $request->query('token');
if(is_null($token_str)) { return '参数错误'; }
$token = Token::where('value', $token_str) ->first();
if(is_null($token)) { return '参数错误'; }
if($token->status !== 1) { return ''; }
$user = Users::where('id', $token->uid) ->first();
return '登陆成功,用户名:' . $user->nickname; } }
|
app\Http\Controllers\WechatController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| <?php
namespace App\Http\Controllers;
use App\Models\Users; use App\Models\Token; use EasyWeChat\Factory; use Illuminate\Http\Request;
class WechatController extends Controller { public function index(Request $request) { $config = [ 'app_id' => 'wx120dfab834000d0c', 'secret' => '2126be89ea53de4976c4cebdac79a74f', 'token' => 'hongfs', ];
$app = Factory::officialAccount($config);
$app->server->push(function ($message) use($app) { // 接收到事件信息 if(isset($message['MsgType']) && $message['MsgType'] === 'event') { // 事件名称 $event_name = $message['Event'];
if(in_array($event_name, ['SCAN', 'subscribe']) && $message['EventKey'] !== null) { $token_str = $message['EventKey'];
if($event_name === 'subscribe') { $token_str = substr($token_str, 8); }
$token = Token::where('value', $token_str) ->first();
if(is_null($token)) { return 'token 不存在'; }
if($token->status !== 0 || strtotime($token->created_at) + 600 < time()) { return 'token 已失效'; }
$openid = $message['FromUserName'];
$user_data = $app->user->get($openid);
$user = Users::where('openid', $openid) ->first();
if(is_null($user)) { $user = new Users; $user->openid = $openid; $user->nickname = $user_data['nickname'];
if(!$user->save()) { return '用户创建失败'; } }
$token->uid = $user->getKey(); $token->status = 1;
if(!$token->save()) { return 'token 更改失败'; }
return '登陆成功'; } }
return 'Hi'; });
$response = $app->server->serve();
return $response; } }
|
视图
resources\views\welcome.blade.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>微信扫码登录</title> <link rel="stylesheet" href="https://lib.baomitu.com/bulma/0.8.0/css/bulma.min.css" /> </head> <body> <section class="hero is-info is-large"> <div class="hero-body"> <div class="container" style="display: flex; flex-direction: column; align-items: center; width: 200px;"> <h1 class="title"> <img src="{{ $image_url }}" /> </h1> <h2 class="subtitle"> 二维码只有五分钟有效期,超时刷新。 </h2> </div> </div> </section> <script src="https://lib.baomitu.com/jquery/3.5.0/jquery.min.js"></script> <script> var t; t = setInterval(_ => { $.post(`?token={{ $token }}`, (response) => { if(response.length) { alert(response); clearInterval(t); } }); }, 1000); </script> </body> </html>
|
路由
routes\web.php
1 2 3 4 5 6
| <?php
use Illuminate\Support\Facades\Route;
Route::any('/', 'IndexController@index'); Route::any('/wechat', 'WechatController@index');
|
测试
其他
服务端接收推送前要进行服务器配置。微信验证 Token 是 GET 请求,推送是 POST 请求。
另外微信还提供测试号:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login