三星级头皮理疗老师 發表於 2025-4-25 15:44:00

记录---扫码登录分析与实现

<div>
<div>
<h1 data-id="heading-0">🧑‍💻 写在开头</h1>
<p>点赞 + 收藏 === 学会🤣🤣🤣</p>
<h2 data-id="heading-0">前言</h2>
<blockquote>
<p>随着移动设备的普及,扫码登录成为一种便捷且安全的用户身份验证方式。它不仅免去了用户输入账号密码的繁琐步骤,还能减少密码泄露的风险。在当今的前端开发中,扫码登录已经成为许多应用的标配功能。那么,作为一名前端开发者,该如何实现这一功能呢?本文将从技术原理、前端实现细节以及实际案例 demo 出发,带领大家完成扫码登录功能的实现。</p>
</blockquote>
<h2 data-id="heading-1">背景与概述</h2>
<h4 data-id="heading-2">什么是扫码登录?</h4>
<p>扫码登录是一种利用二维码技术进行用户身份验证的登录方式。它通过在客户端生成一个唯一标识的二维码(通常包含登录相关信息,如会话ID、加密的认证信息等),用户使用支持扫码的设备(如手机上的App)扫描二维码并完成授权。后台验证通过后,客户端即完成登录。<br>
扫码登录的核心特征在于免去手动输入账号密码的步骤,借助移动设备的便捷性实现快速认证。</p>
<p>以下是淘宝网页版的扫码登录图示:</p>

</div>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202504/2149129-20250425154128288-1673645113.png" alt="" loading="lazy"></p>
<div>
<div>
<p><em><strong>扫码登录的应用场景和优势</strong></em></p>
<ul>
<li>
<p><strong>应用场景</strong></p>
<ul>
<li><strong>Web端登录</strong><br>
用户通过PC浏览器访问一个网站,使用手机扫描二维码进行快速登录。例如微信网页版、淘宝Web端等。</li>
<li><strong>App间互联</strong><br>
利用一个App扫码登录另一个App或平台。例如支付宝扫码登录其他应用服务。</li>
<li><strong>设备绑定</strong><br>
在IoT设备(如智能电视、智能音箱)上扫码登录,通过手机验证绑定账号信息。</li>
<li><strong>共享设备</strong><br>
在共享场景(如网吧、自助终端等)中,扫码登录可以减少物理输入的麻烦,保护隐私。</li>


</ul>


</li>
<li>
<p><strong>优势</strong></p>
<ul>
<li><strong>高效便捷</strong><br>
通过扫描二维码,用户免去输入账号密码的麻烦,登录时间大幅缩短。</li>
<li><strong>跨设备登录</strong><br>
用户可以轻松在不同设备间切换登录状态,例如从手机登录Web端。</li>
<li><strong>安全性提高</strong><br>
用户无需在不受信任的设备上输入密码,有效降低密码被劫持或泄露的风险。</li>
<li><strong>提升用户体验</strong><br>
适配移动端普及的趋势,为用户提供更现代化、友好的交互方式。</li>


</ul>


</li>


</ul>
<h4 data-id="heading-3">相关技术栈与基础知识</h4>
<ul>
<li>
<p><strong>前端技术栈</strong></p>
<ul>
<li>
<p><strong>二维码生成</strong><br>
使用库如<code>qrcode</code>(JavaScript)、<code>qrcode.react</code>(React专用)生成二维码。</p>


</li>
<li>
<p><strong>前端通信</strong></p>
<ul>
<li><strong>轮询</strong>:定时请求后端接口,查询登录状态。</li>
<li><strong>WebSocket</strong>:实时通信,前端实时获取二维码状态更新。</li>


</ul>


</li>
<li>
<p><strong>UI组件库</strong><br>
使用组件库(如Ant Design、Element-UI)优化交互和页面设计。</p>


</li>
<li>
<p><strong>状态管理</strong><br>
使用Pinia、Vuex或Redux管理扫码登录状态,简化页面间的状态流转。</p>


</li>


</ul>


</li>
<li>
<p><strong>后端支持</strong></p>
<ul>
<li>
<p><strong>二维码内容生成</strong><br>
后端生成二维码中包含的唯一标识(如UUID或会话ID)。</p>


</li>
<li>
<p><strong>登录状态存储</strong><br>
使用Redis等高效存储方式记录二维码状态(未扫描、已扫描、已确认登录)。</p>


</li>
<li>
<p><strong>API设计</strong></p>
<ul>
<li>二维码状态接口:前端轮询或通过WebSocket获取状态更新。</li>
<li>授权接口:处理用户扫码后的授权请求并返回结果。</li>


</ul>


</li>


</ul>


</li>
<li>
<p><strong>安全性与加密技术</strong></p>
<ul>
<li>使用HTTPS加密通信,确保二维码数据在传输中的安全性。</li>
<li>在二维码中加入加密信息,防止二维码被伪造或劫持。</li>
<li>设置二维码的有效期,减少被重复使用的风险。</li>


</ul>


</li>


</ul>
<h2 data-id="heading-4">扫码登录的原理</h2>
<ul>
<li>
<p><strong>前端生成二维码</strong></p>
<ul>
<li><strong>初始化请求</strong><br>
前端向后端发送请求,获取一个唯一标识符(如<code>UUID</code>)或加密的会话信息。<br>
后端记录该标识符和二维码的初始状态(如“未扫码”),并存储在Redis等高效存储工具中。</li>
<li><strong>生成二维码</strong><br>
前端使用二维码生成库(如<code>qrcode</code>)将唯一标识符编码成二维码,展示在页面上供用户扫码。</li>


</ul>


</li>
<li>
<p><strong>用户扫码并授权</strong></p>
<ul>
<li>用户使用移动设备的App(如微信、支付宝或特定的业务App)扫描二维码。</li>
<li>移动端通过二维码中的唯一标识符与后端交互,发送用户的登录授权请求。</li>
<li>后端校验用户身份(如检查是否已登录App、确认授权操作),更新二维码状态为“已授权”,并记录用户信息。</li>


</ul>


</li>
<li>
<p><strong>后端通知登录状态更新</strong></p>
<ul>
<li>
<p>后端根据前端的查询(轮询或WebSocket推送)返回二维码状态的实时更新:</p>
<ul>
<li>如果状态为“已授权”,前端完成用户登录操作(如跳转到主页面)。</li>
<li>如果二维码过期,前端更新页面提示用户刷新二维码。</li>


</ul>


</li>


</ul>


</li>


</ul>
<p><em><strong>流程图解如下:</strong></em></p>

</div>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202504/2149129-20250425154154889-1243306661.png" alt="" loading="lazy"></p>
<div>
<div>
<h4 data-id="heading-5">前后端交互的关键点</h4>
<h5 data-id="heading-6">1. <strong>二维码生成与信息绑定</strong></h5>
<ul>
<li>
<p><strong>二维码内容设计</strong></p>
<ul>
<li>通常二维码包含一个唯一标识符(如<code>UUID</code>)或加密的信息,用于标识当前会话。</li>
<li>可使用Base64进行编码或使用AES加密,防止二维码被伪造。</li>
</ul>
</li>
<li>
<p><strong>后端存储二维码状态</strong></p>
<ul>
<li>初始状态:未扫码。</li>
<li>已扫码:用户已扫描但未确认授权。</li>
<li>已授权:用户已确认授权,登录成功。</li>
<li>已过期:二维码有效期已过。</li>
<li>使用Redis等存储状态,利用其高效的键值对操作支持实时更新。</li>
</ul>
</li>
<li>
<p><strong>二维码有效期管理</strong></p>
<ul>
<li>后端为二维码设置一个有效期(如1-5分钟),过期后需重新生成,防止二维码被长期滥用。</li>
</ul>
</li>
</ul>
<h5 data-id="heading-7">2. <strong>WebSocket或轮询实现实时更新</strong></h5>
<ul>
<li>
<p><strong>轮询</strong></p>
<ul>
<li>
<p>前端定时(如每1-5秒)请求后端接口,检查二维码状态:</p>
<ul>
<li>API 示例:<code>GET /qr-status?uuid=xxxx</code>。</li>
<li>响应示例:<code>{ "status": "authorized", "user": { "id": 123, "name": "John" } }</code>。</li>
</ul>
</li>
<li>
<p>简单易实现,但对后端压力较大,不适合高并发场景。</p>
</li>
</ul>
</li>
<li>
<p><strong>WebSocket</strong></p>
<ul>
<li>
<p>前端与后端建立长连接,后端通过WebSocket实时推送二维码状态更新。</p>
</li>
<li>
<p>优势:实时性强,减少无效请求,提升性能。</p>
</li>
<li>
<p>示例流程:</p>
<ol>
<li>
<p>前端通过<code>Socket.IO</code>与后端建立连接:</p>
</li>
</ol></li>
</ul>
</li>
</ul>
</div>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">const socket = io('https://example.com');
socket.emit('subscribe', { uuid: 'xxxx' });
</pre>
</div>
<p>  后端监听并推送状态变化:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">io.to('xxxx').emit('qrStatus', { status: 'authorized' });
</pre>
</div>
<p>  前端接收更新并处理:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">socket.on('qrStatus', (data) =&gt; {
    if (data.status === 'authorized') {
      window.location.href = '/dashboard';
    }
});</pre>
</div>
<p><em>流程状态显示</em></p>
<p><code>初始状态:未扫码(new)</code></p>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202504/2149129-20250425154243675-212962890.png" alt="" loading="lazy"></p>
<p>&nbsp;已扫码,但未确认(scaned):</p>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202504/2149129-20250425154254308-1150920251.png" alt="" loading="lazy"></p>
<p><code>扫码后点击确认登录(authorized)</code></p>
<p>淘宝跳转太快了,小编截不到图片,想象一下...(就是执行登录后页面的跳转)</p>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202504/2149129-20250425154308315-117870601.png" alt="" loading="lazy"></p>
<p>&nbsp;二维码过期(expired):</p>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202504/2149129-20250425154320438-1956693681.png" alt="" loading="lazy"></p>
<p>&nbsp;</p>
<ul>
<li>
<p>选型建议</p>
<ul>
<li>小型应用或低频访问场景可用轮询。</li>
<li>高并发或需要实时响应的场景建议使用WebSocket。</li>
</ul>
</li>
</ul>
<h3 data-id="heading-8">demo 代码示例</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">&lt;template&gt;
    &lt;div class="app-container"&gt;
      &lt;div class="register"&gt;
            &lt;!-- 登录输入部分 --&gt;
            &lt;div class="register-content"&gt;
                  &lt;!-- 扫码登录部分 --&gt;
                  &lt;div class="qr-login"&gt;
                        &lt;canvas ref="qrCanvas" class="qr-code"&gt;&lt;/canvas&gt;
                        &lt;p v-if="qrStatus === 'waiting'"&gt;请使用移动设备扫码登录&lt;/p&gt;
                        &lt;p v-if="qrStatus === 'scanned'"&gt;二维码已扫描,请确认登录&lt;/p&gt;
                        &lt;p v-if="qrStatus === 'expired'"&gt;二维码已过期,请刷新页面&lt;/p&gt;
                        &lt;el-button v-if="qrStatus === 'expired'" @click="generateQRCode"&gt;重新生成二维码&lt;/el-button&gt;
                  &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { ref, onMounted } from 'vue';
import QRCode from 'qrcode';
import axios from 'axios';

const qrCanvas = ref(null);
const qrStatus = ref('waiting'); // 状态:waiting, scanned, expired
const qrUUID = ref('');
let pollInterval = null;

// 生成二维码
const generateQRCode = async () =&gt; {
    try {
      // 请求后端生成UUID
      const response = await axios.get('/api/qr-code'); // 替换为真实后端接口
      qrUUID.value = response.data.uuid;

      // 使用二维码库生成二维码
      QRCode.toCanvas(qrCanvas.value, response.data.url, { width: 200 });

      // 启动状态轮询
      qrStatus.value = 'waiting';
      startPolling();
    } catch (error) {
      console.error('生成二维码失败', error);
    }
};

// 启动轮询
const startPolling = () =&gt; {
    pollInterval = setInterval(async () =&gt; {
      try {
            const response = await axios.get(`/api/qr-status/${qrUUID.value}`);
            qrStatus.value = response.data.status;

            if (response.data.status === 'authorized') {
                clearInterval(pollInterval);
                window.location.href = '/dashboard'; // 登录成功跳转
            }

            if (response.data.status === 'expired') {
                clearInterval(pollInterval);
            }
      } catch (error) {
            console.error('轮询失败', error);
      }
    }, 3000); // 每3秒请求一次
};

// 页面加载时生成二维码
onMounted(() =&gt; {
    generateQRCode();
});
&lt;/script&gt;

&lt;style scoped&gt;
.qr-login {
    text-align: center;
    margin-top: 20px;
}
.qr-code {
    margin: 10px auto;
    display: block;
}
&lt;/style&gt;</pre>
</div>
<h3 data-id="heading-9">结语</h3>
<blockquote>
<p>扫码登录作为一种便捷又安全的登录方式,已经成为许多应用和服务的首选解决方案。在实际开发中,我们不仅要关注功能实现,更要注重用户体验和安全性。希望通过本文的解析和示例代码,您能够轻松上手,实现属于您项目的扫码登录功能。</p>
</blockquote>
<div>
<h2>本文转载于:https://juejin.cn/post/7447065417322201128</h2>
</div>
<h3 id="tid-D8HBxE">如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。</h3>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202501/2149129-20250122165814748-630765389.png" alt="" loading="lazy"></p>
</div>
</div><br><br>
来源:https://www.cnblogs.com/smileZAZ/p/18846842
頁: [1]
查看完整版本: 记录---扫码登录分析与实现