新手必看

准备工作

主要流程

初始化项目

> composer create-project --prefer-dist laravel/laravel joker
> cd joker
> php artisan key:generate
Application key set successfully.
> php artisan --version
Laravel Framework 8.34.0

引入laravel-websockets软件包

laravel-websockets 是一款封装好了 websocket 服务的软件包。其特点是通过拦截 pusher 的请求来摆脱对 pusher 服务商(国外)的依赖。还具有调试面板,实时统计信息,甚至允许您创建自定义 WebSocket 控制器等能力。以下执行了两步操作,首先引入软件包,然后发布软件包的配置文件和数据库迁移文件。你可以在config文件夹中找到websockets.php配置文件。数据库迁移文件可以先不管。

> composer require beyondcode/laravel-websockets
> php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider"

启动websocket监听

我们需要在.env文件中配置几个参数,PUSHER_APP_IDPUSHER_APP_KEYPUSHER_APP_SECRET可以随便写,与文档中提到的 pusher 无关。因为有 Laravel-websockets在程序里拦截了pusher的转发动作,会将事件转发到本地。BROADCAST_DRIVER指定广播驱动为pusher固定值。LARAVEL_WEBSOCKETS_PORT是指 socket 监听的端口号,一定要让这个端口可以被外网访问:

// .env
BROADCAST_DRIVER=pusher
PUSHER_APP_ID=joker
PUSHER_APP_KEY=joker
PUSHER_APP_SECRET=joker
LARAVEL_WEBSOCKETS_PORT=2020

配置好以后,我们就可以启动 socket 监听程序了。虽然上面的.env文件中,我们已经配置好2020端口了,但这里的启动命令中,我们还是需要--port=2020手动指定端口号,否则默认是6001端口:

> php artisan websockets:serve --port=2020
Starting the WebSocket server on port 2020...

看,启动一个 socket 监听程序就是这么简单。为了验证是否能进行通信,我们在地址栏中输入http://<your.host>/laravel-websockets,然后会显示一个在Laravel-websockets中已经注册好的页面:

我们点击那个connect按钮,如果如下图所示,则说明可以正常通信了:

创建两个页面

routes/web.php中新增web路由,并创建相应的view视图

Route::get('/hello', function () {
// 首页显示二维码
return view('hello');
}); Route::get('login', function () {
// 扫码后,手机显示的登陆页
return view('login');
}); Route::post('/login', function () {
// 这里先空着,后面会补充
return 'Logined';
});

建立socket连接

我们需要在app/resources/views/hellow.blade.php中先引入一些JS工具,然后编写一些JS代码。

<!DOCTYPE html>
<html>
<head>
<title>Hello</title>
      <!-- 引入jQuery工具 -->
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
       <!-- 引入二维码工具 -->
<script src="https://cdn.bootcdn.net/ajax/libs/jquery.qrcode/1.0/jquery.qrcode.min.js"></script>
       <!-- 引入laravel-echo工具,其实使用Larave自带的也可以。但是,使用自带的还需要用到node前端构建工具,我这里只简单的演示后端实现过程,就不用node了 -->
<script src="https://cdn.bootcdn.net/ajax/libs/laravel-echo/1.10.0/echo.iife.js"></script>
       <!-- 引入pusher工具,pusher是Laravel-echo底层,Laravel-echo是pusher的一层封装 -->
<script src="https://cdn.bootcdn.net/ajax/libs/pusher/7.0.3/pusher.min.js"></script>
</head>
<body>
<h1>二维码</h1>
</body>
<script type="text/javascript"> // 简单模拟一个 uuid 唯一身份码,为了后端广播时,不会广播给错人
var uuid = Math.random().toString(36); // 初始化 laravel-echo 插件
window.Echo = new Echo({
// 这里是固定值 pusher
broadcaster: 'pusher',
// 这里要和你在 .env 中配置的 PUSHER_APP_KEY 保持一致
key: 'joker',
wsHost: location.hostname,
// 这里是我们在上一步启动 socket 监听的端口
wsPort: 2020,
// 这个也要加上
forceTLS: false,
}); // 我们随便监听一个频道,这个频道在项目还不存在,但不影响建立 socket 连接
Echo.channel('abcdefg.'+uuid)
// 随便监听一个事件,这个事件在项目中还不存在,但不影响建立 socket 连接
.listen('LoginedEvent', (e) => {
console.log(e);
}); // 显示一个二维码,内容是一个登陆页地址,后面拼接 uuid。这个 uuid 会在后面广播中用到,用来给监听此 uuid 频道的 socket 发送数据
$("body").qrcode(location.origin+"/login?uuid="+uuid); </script>
</html>

代码中已经注释的比较清晰了,我们保存文件,然后在浏览器中输入http://<your.host>/hello点击回车。我们打开浏览器的开发者工具,找到WS选项,我们会看到 socket 已经与服务端进行了通信。再看下服务端,会显示连接者的一些信息:

手机端扫码登陆

这一步我们只需要做两件事,写一个简单的登陆页面,然后实现登陆验证:

简单的登陆页

我们打开app/resources/views/login.blade.php视图文件,写入以下内容:

<!DOCTYPE html>
<html>
<head>
<title>Login</title>
       <!-- 引入jQuery工具 -->
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<input type="text" name="username">
<input type="text" name="password">
<button>登陆</button>
</body>
<script type="text/javascript"> // 请求登陆接口
$('button').click(function(event) {
// 还记得前面扫码的链接里,带有 uuid 参数吗。简单的获取 url 中 uuid 参数
var uuid = location.search.substring(1).split('=')[1];
// 请求登陆接口
$.ajax({
// 这个登陆接口就是上面 routes/web.app 中定义好的路由。因为是 POST 请求,所以会进入 Route::post() 定义的路由中
url: location.origin+'/login',
type: 'POST',
dataType: 'json',
data: {
username: $('input[name="username"]').val(),
password: $('input[name="password"]').val(),
uuid: uuid,
// Laravel 默认带有 csrf_token 验证,所以这里要加 _token 变量
_token: '{{ csrf_token() }}',
},
success: function(data){
console.log(data);
}
});
}); </script>
</html>

很简单的一个页面,我们用微信扫一扫看下页面效果:

哈哈,虽然很丑,但很简洁不是吗?接下来,我们要进行后台代码的完善工作了。

登陆验证

进入routes/web.php中,修改Route::post()路由。为了简化代码流程,我就直接在路由中写登陆逻辑了, 但平时一定不要这么干

Route::post('/login', function (\Illuminate\Http\Request $request) {
// 为了尽可能简化流程,我们就不执行数据库查询的逻辑了,直接在 session 中写入信息吧
session([
'login_info' => $request->all(),
]);
// 返回响应
return response([
'code' => 0,
'message' => '登陆成功',
'data' => session('login_info'),
]);
});

这应该是最简化的登陆逻辑了吧,我们来小测一下,在http://<your.host>/login页面中,在两个输入框中写入内容,然后点击登陆,看下返回结果:

确实返回了我们输入的信息哈。一步一个脚印,一步一回头,每完成一个小功能呢,就小测一下,个人习惯哈。

服务端触发登陆事件

这一环节,做的事情也不是很多,我们一步一步来。首先要把config/app.php中的App\Providers\BroadcastServiceProvider::class注释打开:

routes/channels.php中加入我们最开始,在前端JS中写的监听频道:

Broadcast::channel('abcdefg.{uuid}', function () {
return true;
});

config/broadcasting.php中的connections.pusher下加入两个配置信息:

'connections' => [
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => env('PUSHER_APP_CLUSTER'),
'useTLS' => true,
// 拦截 pusher 的广播后,转发到目标 ip
'host' => '127.0.0.1',
// 转发的端口,就是我们之前在 .env 文件中配置的 2020 端口
'port' => env('LARAVEL_WEBSOCKETS_PORT', 6001),
],
];
];

创建登陆事件

> php artisan make:event LoginedEvent
Event created successfully.

执行以上代码后,我们可以在app/Events文件夹中找到LoginedEvent.php文件。

在事件中发送广播

只需在上面新建的app/Events/LoginedEvent.php中,进行很小的改动就可以了。

<?php

namespace App\Events;

// 首先要让这个事件类实现 ShouldBroadcast 接口,也就是在类名后加上 implements ShouldBroadcast 即可。
class LoginedEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels; // 只有 punlic 属性的变量会被广播到指定的频道中,所以这里要使用 public 关键字修饰你需要广播的变量
public $logined_info; public function __construct($logined_info)
{
$this->logined_info = $logined_info;
} // 实现 ShouldBroadcast 接口后,就必须实现这个方法,系统会自动将广播发送到这个方法中定义的频道中
public function broadcastOn()
{
// 这里我们改为 Channel。原 PrivateChannel 是私有频道,未登录时前端无法监听
// 这里的频道要与 routes/channels.php 中定义的频道格式保持一致
return new Channel('abcdefg.'.$this->logined_info['uuid']);
}
}

触发

我们再回到登陆验证的路由中,在保存 session 数据后,加入触发登陆事件的代码:

Route::post('/login', function (\Illuminate\Http\Request $request) {

    session([
'login_info' => $request->all(),
]);
// 触发登陆事件
event(new \App\Events\LoginedEvent(session('login_info'))); return response([
'code' => 0,
'message' => '登陆成功',
'data' => session('login_info'),
]);
});

好了,我们再来验证下,登陆后在 socket 中是否会收到广播内容。这里我们可以同时打开http://<your.host>/loginhttp://<your.host>/laravel-websockets页面,然后用手机微信的扫一扫功能扫描页面上的二维码。在手机上任意输入内容,点击登陆按钮,看一下是否有新的广播数据:

主要流程已经通了,现在离完成我们的目标已经近在咫尺了,加油!

页面跳转(重点)

现在我们来调整下app/Events/LoginedEvent.php事件和routes/web.php路由文件中的/hello部分。主要目的是:

在手机端触发的登陆事件中,通过广播将登陆后的session_id返回给 web 网页前端。前端拿到后再将其拼接到路由中,执行页面跳转。后端会将 url 中的session_id参数获取到,然后将此 session id 保存到此次 web 端请求的 session id 中。也就是使 web 端网页与手机端使用同一个 session id 。

注意:在操作 session 数据时,如果你在框架 请求结束前 使用类似dd()die()等函数,则在此之前操作的 session 数据不会进行持久化保存。详细原因可参考源码:

这里仅作为演示无需重复登录即可同步登陆信息的实现,其中会有很多安全问题,这里就先不考虑了,大佬们勿喷。先调整下app/Events/LoginedEvent.php文件:

class LoginedEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels; // 只有 punlic 属性的变量会被广播到指定的频道中,所以这里要使用 public 关键字修饰你需要广播的变量
public $logined_info;
// 加入新的变量 $session_id
public $session_id; public function __construct($logined_info)
{
$this->logined_info = $logined_info;
// 获取当前的 session id
$this->session_id = session()->getId();
} public function broadcastOn()
{
return new Channel('abcdefg.'.$this->logined_info['uuid']);
}
}

routes/web.php文件中的/hello路由里面加入替换 session id 的代码,这一步很关键:

Route::get('/hello', function (\Illuminate\Http\Request $request) {
if($request->get('session_id')){
// 将手机端登陆的 session_id 设置到当前页面的 session 中
session()->setId($request->get('session_id'));
// 重新读取 session 数据。其实就是将手机端登陆后的 session 内容读取到当前页面的 session 中
session()->start();
// 这里只是做个提示
echo session('login_info.username')." 已通过手机扫码登录";
}
return view('hello');
});

最后是app/resources/views/hellow.blade.php视图文件,我们只需要修改监听到事件后,执行回调的部分:

        Echo.channel('abcdefg.'+uuid)
.listen('LoginedEvent', (e) => {
console.log(e);
// 从登陆事件广播出来的数据中,取出 session_id 字段
var session_id = e.session_id;
// 拼接好参数后,跳转到指定页面,也就是当前页面
location.href = location.origin+'/hello?session_id='+session_id;
});

最后看下结果吧!

此时已经实现手机端页面与 web 端页面使用的是同一个 session 数据了。记得在改后端代码时,尽量重启一下laravel-wesocket的 http 服务,以免出现改代码后,没有生效的问题。

相关文章

利用laravel-echo主动向服务端发送消息,实现在线状态管理:主要讲述了laravel-echo如何主动向服务器发送消息,并在后端编写自己的控制器逻辑。

利用websocket实现手机扫码登陆后,同步登陆信息到web端页面的更多相关文章

  1. 【flask】登陆后返回之前重定向跳转的页面

    登陆后返回之前重定向跳转的页面 一.前言 实现强制跳转到登陆页面,登陆后返回之前的页面的功能.网上跳登陆页面的很多:返回之前页面功能没多少.这里我只是用了自己的方法,有缺点和其他方法也请指点!(´ε` ...

  2. 12.18 微信扫码获取openid与登陆

    官方文档: https://open.weixin.qq.com/ 1.先获取code 1-1 配置项目配置文件 wechat: mpAppId: wxd898fcb01713c658 mpAppSe ...

  3. 在做微信分享到朋友圈时,手机扫码报config:invalid signature,分享后后正常的问题,是url问题

    是按照以下步骤检查的 除了ACCESS_TOKEN没有缓存其他都可以 如果是invalid signature签名错误.建议按如下顺序检查: 1.确认签名算法正确,可用 http://mp.weixi ...

  4. python-Web-django-qq扫码登陆

    1.建路由 2.写qq登录的a链接 3 在控制器的loginQq的方法:拼接url,跳转到这个url: 去:https://graph.qq.com/oauth2.0/authorize?respon ...

  5. 微信扫码登陆(JAVA)

    在web端用到weChat扫码登录,在手机扫码登陆成功后,跳转到相应的界面. 1.第一步请求code 调用接口:https://open.weixin.qq.com/connect/qrconnect ...

  6. 分享一下,PHP实现第四方QQ微信扫码登陆,不接入qq互联以及微信开发者平台就可以实现用户对接鹅厂,phpQQ微信扫码登陆

    自己抓的QQ包以及整合了网上一些已经封装好了的代码具体如下:QQ: <?php class QQ extends Curl_Api { //获取登录验证码 public function QRc ...

  7. 微信扫码登陆js

    先贴一个微信开发文档教程 https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.ht ...

  8. Java 语言实现简易版扫码登录

    基本介绍 相信大家对二维码都不陌生,生活中到处充斥着扫码登录的场景,如登录网页版微信.支付宝等.最近学习了一下扫码登录的原理,感觉蛮有趣的,于是自己实现了一个简易版扫码登录的 Demo,以此记录一下学 ...

  9. 初涉扫码登录:edusoho实现客户端扫码登录(简版)

    一.项目简介及需求 edusoho是一套商业版的在线教育平台,项目本身基于symfony2框架开发,现在有一款自己的APP,要求在不多修改edusoho自身代码的基础上,实现客户端对PC端扫码登录.不 ...

随机推荐

  1. 保姆级教程:VsCode调试docker中的NodeJS程序

    最近在写NodeJS相关的项目,运行在docker容器中,也是想研究一下断点调试,于是查阅相关资料,最终顺利配置好了. 首先我选择了VsCode作为ide,并用VsCode来做NodeJS可视化deb ...

  2. 真实本人亲测Elasticsearch未授权访问漏洞——利用及修复【踩坑指南到脱坑!】

    如要转载请注明出处谢谢: https://www.cnblogs.com/vitalemontea/p/16105490.html 1.前言 某天"发现"了个漏洞,咳咳,原本以为这 ...

  3. CA周记 - 带你进⼊ OpenAI 的世界

    2021年11月的 Microsoft Ignite , 微软带来了全新的 Azure OpenAI Service,通过新的 Azure 认知服务能够访问 OpenAI 强大的 GPT-3 模型 . ...

  4. 攻防世界-MISC:wireshark-1

    这是攻防世界高手进阶区的第五题,题目如下: 点击下载附件一,得到一个压缩包,解压后得到一个流量包,用wireshark打开,分组字节流搜索字符串flag password的值即是flag,所以这道题的 ...

  5. Bugku CTF练习题---杂项---隐写3

    Bugku CTF练习题---杂项---隐写3 flag:flag{He1l0_d4_ba1} 解题步骤: 1.观察题目,下载附件 2.打开图片,发现是一张大白,仔细观察一下总感觉少了点东西,这张图好 ...

  6. K-MEANS算法【聚类】

    可视化 聚类 最简单实用的聚类算法:K-MEANS算法 K值:数据分成几份 质心:簇的中心点 优化目标:K个簇的(每个簇中的每个点距离质心的距离)的和最小 ci中心点,x个别点 工作流程: 设置K值, ...

  7. 99乘法表 java for循环

    public static void main(String[] args) { //0-100的奇数和偶数和 int jsum=0; int osum=0; for (int i = 0; i &l ...

  8. 【PyTorch】常用的神经网络层汇总(持续补充更新)

    1. Convolution Layers 1.1 nn.Conv2d (1)原型 torch.nn.Conv2d(in_channels, out_channels, kernel_size, st ...

  9. 【机器学习基础】无监督学习(3)——AutoEncoder

    前面主要回顾了无监督学习中的三种降维方法,本节主要学习另一种无监督学习AutoEncoder,这个方法在无监督学习领域应用比较广泛,尤其是其思想比较通用. AutoEncoder 0.AutoEnco ...

  10. 并发编程系列之Lock锁可重入性与公平性

    一.相似之处:Lock锁 vs Synchronized 代码块 Lock锁是一种类似于synchronized 同步代码块的线程同步机制.从Java 5开始java.util.concurrent. ...