利用websocket实现手机扫码登陆后,同步登陆信息到web端页面
新手必看
准备工作
主要流程
初始化项目
> 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_ID
、PUSHER_APP_KEY
、PUSHER_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>/login
和http://<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端页面的更多相关文章
- 【flask】登陆后返回之前重定向跳转的页面
登陆后返回之前重定向跳转的页面 一.前言 实现强制跳转到登陆页面,登陆后返回之前的页面的功能.网上跳登陆页面的很多:返回之前页面功能没多少.这里我只是用了自己的方法,有缺点和其他方法也请指点!(´ε` ...
- 12.18 微信扫码获取openid与登陆
官方文档: https://open.weixin.qq.com/ 1.先获取code 1-1 配置项目配置文件 wechat: mpAppId: wxd898fcb01713c658 mpAppSe ...
- 在做微信分享到朋友圈时,手机扫码报config:invalid signature,分享后后正常的问题,是url问题
是按照以下步骤检查的 除了ACCESS_TOKEN没有缓存其他都可以 如果是invalid signature签名错误.建议按如下顺序检查: 1.确认签名算法正确,可用 http://mp.weixi ...
- python-Web-django-qq扫码登陆
1.建路由 2.写qq登录的a链接 3 在控制器的loginQq的方法:拼接url,跳转到这个url: 去:https://graph.qq.com/oauth2.0/authorize?respon ...
- 微信扫码登陆(JAVA)
在web端用到weChat扫码登录,在手机扫码登陆成功后,跳转到相应的界面. 1.第一步请求code 调用接口:https://open.weixin.qq.com/connect/qrconnect ...
- 分享一下,PHP实现第四方QQ微信扫码登陆,不接入qq互联以及微信开发者平台就可以实现用户对接鹅厂,phpQQ微信扫码登陆
自己抓的QQ包以及整合了网上一些已经封装好了的代码具体如下:QQ: <?php class QQ extends Curl_Api { //获取登录验证码 public function QRc ...
- 微信扫码登陆js
先贴一个微信开发文档教程 https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.ht ...
- Java 语言实现简易版扫码登录
基本介绍 相信大家对二维码都不陌生,生活中到处充斥着扫码登录的场景,如登录网页版微信.支付宝等.最近学习了一下扫码登录的原理,感觉蛮有趣的,于是自己实现了一个简易版扫码登录的 Demo,以此记录一下学 ...
- 初涉扫码登录:edusoho实现客户端扫码登录(简版)
一.项目简介及需求 edusoho是一套商业版的在线教育平台,项目本身基于symfony2框架开发,现在有一款自己的APP,要求在不多修改edusoho自身代码的基础上,实现客户端对PC端扫码登录.不 ...
随机推荐
- android软件简约记账app开发day09-主页面模块,收支记账信息的展示
android软件简约记账app开发day09-主页面模块,收支记账信息的展示 我们第一天已经绘制了记账条目的界面,也在主界面设置了LietView来展示记账条目,今天来实现记账后再主界面的展示效果 ...
- [DEBUG] QAT Nginx for docker 部署时"--with-ld-opt"出错
layout: post title: [DEBUG] QAT Nginx for docker 部署时"--with-ld-opt"出错 subtitle: 记一次debug经历 ...
- 华为麒麟团队力造的Python,整整26G
华为团队力造的Python,下面链接自取 https://docs.qq.com/doc/DRkZvRkxvb292c2Vz
- 记一次Linux Centos7病毒清理
记一次在工作中测试环境下中病毒的处理解决办法,都说linux系统非常安全,但是很多人百年一遇的病毒被我遇上了,公司三台测试环境服务器中招. 最开始系统突然变得很卡,使用top命令查看资源占用情况,发现 ...
- Promql基础语法2
数据样本 直方图类型 delta函数 运算操作 数学运算 node_disk_info / 100 当瞬时向量与标量之间进行数学运算时,数学运算符会依次作用域瞬时向量中的每一个样本值,从而得到一组新的 ...
- Runable与Callable的区别
Runable与Callable的区别: public interface Callable<V> { V call() throws Exception;//V是Callable返回值的 ...
- FreeRTOS --(5)内存管理 heap4
FreeRTOS 中的 heap 4 内存管理,可以算是 heap 2 的增强版本,在 <FreeRTOS --(3)内存管理 heap2>中,我们可以看到,每次内存分配后都会产生一个内存 ...
- Java多线程——实现
"java多线程的实现--几乎都要和java.lang.Thread打交道" 方式一:继承于Thread类 1.创建一个继承于Thread类的子类 2.重写Thread类的run( ...
- CentOS开机流程详解
一个执着于技术的公众号 开机流程 BIOS: (Basic Input Output System)基本输入输出系统,它是一组固化到计算机内主板上一个ROM芯片上的程序,保存着计算机最重要的基本输入输 ...
- 关于Spring中的useSuffixPatternMatch
背景 spring-boot的版本是2.1.4.RELEASE,spring的版本是5.1.6.RELEASE 一个例子如下: @Configuration @Import(WebMvcAutoCon ...