实现限制同一个账号最多只能在3个客户端(有电脑、手机等)登录(附关键源码)

如上图,我的百度网盘已登录设备列表,有一个手机,2个windows客户端。手机设备有型号、最后登录时间、IP等。windows客户端信息有最后登录时间、操作系统类型、IP地址等。这些具体是如何实现的?下面分别给出android APP中采集手机信息,VUE3中采集电脑信息的实现思路和关键代码。


一、思路
 

通常是通过 Session 管理+Token机制+数据库存储 组合实现的。以下是可能的实现方式:

1. Session 或 Token 机制

  • 服务器为每个客户端生成唯一的 sessiontoken(JWT、OAuth等)。
  • 这些 token 会存储在数据库或缓存系统中(如 Redis)。

2. 记录登录状态

  • 当用户成功登录后,服务器会在数据库中记录 当前设备信息(如 IP、User-Agent、设备 ID)。
  • 还可以给每个 session 生成一个唯一 ID,并记录 设备唯一标识(如浏览器指纹、手机唯一 ID)。

3. 检查并限制登录数量

  • 登录时检查:当用户登录时,服务器会查询数据库或缓存,看当前账号已登录的设备数量是否已达到上限(如 3 个)。
  • 超限处理
    • 方案1:阻止新登录:如果已达上限,则拒绝新的登录请求,并提示“已达到最大设备数量”。
    • 方案2:踢掉旧设备:可以让用户选择踢掉最早登录的设备(删除最早的 session/token)。
    • 方案3:手动管理:提供用户后台,让用户手动管理登录设备,选择登出某个设备。

4. 定期清理过期/无效的 Session

  • 服务器可以设置 session 过期时间(如 7 天无操作自动登出)。
  • 或者在用户 主动登出 时,删除对应的 session/token。

超出最大登录数量时,百度网盘的实现方案是方案1:拒绝新的登录请求,并提示“已达到最大设备数量”。


二、上代码
 

1. Android APP 采集设备信息(Java/Kotlin)

Android 端可以使用 Build 类、TelephonyManagerWifiManager 采集设备信息,例如 设备型号、系统版本、IP 地址、MAC 地址 等。

示例代码 (Kotlin)

import android.content.Context
import android.net.wifi.WifiManager
import android.os.Build
import android.telephony.TelephonyManager
import java.net.NetworkInterface
import java.net.SocketException
import java.util.*fun getDeviceInfo(context: Context): Map<String, String> {val deviceInfo = mutableMapOf<String, String>()// 设备型号deviceInfo["deviceModel"] = Build.MODEL// 设备品牌deviceInfo["deviceBrand"] = Build.BRAND// Android 版本deviceInfo["androidVersion"] = Build.VERSION.RELEASE// 设备唯一 IDdeviceInfo["deviceId"] = Build.SERIAL// 获取 IP 地址(WIFI 或移动网络)val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManagerval wifiInfo = wifiManager.connectionInfoval ip = android.text.format.Formatter.formatIpAddress(wifiInfo.ipAddress)deviceInfo["ipAddress"] = ip// 获取 MAC 地址deviceInfo["macAddress"] = getMacAddress()return deviceInfo
}// 获取 MAC 地址
fun getMacAddress(): String {try {val interfaces = Collections.list(NetworkInterface.getNetworkInterfaces())for (networkInterface in interfaces) {if (!networkInterface.name.equals("wlan0", ignoreCase = true)) continueval macBytes = networkInterface.hardwareAddress ?: return ""return macBytes.joinToString(":") { "%02X".format(it) }}} catch (ex: SocketException) {ex.printStackTrace()}return "02:00:00:00:00:00" // 默认 MAC 地址
}

输出示例

2. Vue3 获取电脑信息

在 Vue3 Web 端,可以获取 IP、浏览器类型、操作系统等,但无法获取 MAC 地址(受浏览器安全限制)。需要配合后端来获取客户端 IP。

示例代码

<script setup>
import { onMounted, ref } from "vue";const deviceInfo = ref({userAgent: "",platform: "",ipAddress: "",
});// 获取本地设备信息
const getDeviceInfo = async () => {deviceInfo.value.userAgent = navigator.userAgent; // 浏览器信息deviceInfo.value.platform = navigator.platform; // 操作系统// 获取公网 IP(需要后端支持)try {const response = await fetch("https://api.ipify.org?format=json");const data = await response.json();deviceInfo.value.ipAddress = data.ip;} catch (error) {console.error("IP 获取失败", error);}
};onMounted(() => {getDeviceInfo();
});
</script><template><div><h3>设备信息</h3><p>操作系统: {{ deviceInfo.platform }}</p><p>浏览器信息: {{ deviceInfo.userAgent }}</p><p>IP 地址: {{ deviceInfo.ipAddress }}</p></div>
</template>

输出示例

3. 将 session 存入 Redis的示例代码

首先,在 Spring Boot 项目的 pom.xml 中引入 Redis 相关依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId>
</dependency>

application.yml 中配置 Redis 连接信息:

spring:redis:host: localhostport: 6379password: ""timeout: 5000session:store-type: redistimeout: 86400  # 1天(可根据需求调整)

在 Java 代码中,我们使用 RedisTemplate 来存储 session,每次用户登录时:

  • 检查是否超出设备数量(例如最多 3 个)
  • 如果超出,踢掉最早的设备
  • 存入 Redis 并设置过期时间
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.concurrent.TimeUnit;@Service
public class SessionService {private final RedisTemplate<String, Object> redisTemplate;private static final int MAX_SESSIONS = 3;  // 限制最大设备登录数public SessionService(RedisTemplate<String, Object> redisTemplate) {this.redisTemplate = redisTemplate;}/*** 记录新的登录 session* @param userId 用户 ID* @param sessionId 新的 session ID* @param deviceInfo 设备信息(IP、设备类型)*/public void addSession(String userId, String sessionId, String deviceInfo) {String key = "user_sessions:" + userId;// 获取当前已存储的 session 列表List<Object> sessions = redisTemplate.opsForList().range(key, 0, -1);if (sessions != null && sessions.size() >= MAX_SESSIONS) {// 超过最大限制,移除最旧的 session(列表最左侧的)redisTemplate.opsForList().leftPop(key);}// 添加新的 sessionredisTemplate.opsForList().rightPush(key, sessionId + ":" + deviceInfo);// 设置 session 过期时间(7 天)redisTemplate.expire(key, 7, TimeUnit.DAYS);}/*** 获取用户已登录的设备列表*/public List<Object> getSessions(String userId) {String key = "user_sessions:" + userId;return redisTemplate.opsForList().range(key, 0, -1);}/*** 删除某个 session(用于手动登出)*/public void removeSession(String userId, String sessionId) {String key = "user_sessions:" + userId;redisTemplate.opsForList().remove(key, 1, sessionId);}
}

测试 API

创建一个 RestController 让前端可以调用:

import org.springframework.web.bind.annotation.*;import java.util.List;@RestController
@RequestMapping("/session")
public class SessionController {private final SessionService sessionService;public SessionController(SessionService sessionService) {this.sessionService = sessionService;}// 添加新的登录 session@PostMapping("/add")public String addSession(@RequestParam String userId, @RequestParam String sessionId, @RequestParam String deviceInfo) {sessionService.addSession(userId, sessionId, deviceInfo);return "Session added successfully!";}// 获取用户的 session 列表@GetMapping("/list")public List<Object> getSessions(@RequestParam String userId) {return sessionService.getSessions(userId);}// 移除指定 session(登出)@PostMapping("/remove")public String removeSession(@RequestParam String userId, @RequestParam String sessionId) {sessionService.removeSession(userId, sessionId);return "Session removed!";}
}

测试示例

启动 Spring Boot 服务器后,可以用 Postmancurl 测试:

① 添加新 session
 

curl -X POST "http://localhost:8080/session/add?userId=123&sessionId=abcd123&deviceInfo=Windows_192.168.1.10"

返回

Session added successfully!

② 获取已登录设备

curl -X GET "http://localhost:8080/session/list?userId=123"

返回

③ 手动登出某个设备

curl -X POST "http://localhost:8080/session/remove?userId=123&sessionId=session1"

返回:Session removed!

总结

功能实现方式
存储 sessionRedis List 数据结构 (opsForList())
限制最多 3 个设备超过 3 个时 leftPop() 删除最旧 session
查询设备range(0, -1) 获取当前 session
登出设备remove() 删除指定 session
Session 过期expire(7, TimeUnit.DAYS) 设置 7 天过期

这样就可以限制用户最多只能在3台设备上登录,并支持手动踢出设备。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/69829.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法基础:贪心|双指针|二分|倍增

贪心 算法思想&#xff1a; 把整个问题分解成多个步骤&#xff0c;在每个步骤都选取当前步骤的最优方案&#xff0c;直到所有步骤结束&#xff1b;每个步骤都不会影响后续步骤。 核心&#xff1a;采取局部最优&#xff0c;最终结果就全局最优。 双指针 反向扫描 同向扫描 二…

在本地校验密码或弱口令 (windows)

# 0x00 背景 需求是验证服务器的弱口令&#xff0c;如果通过网络侧校验可能会造成账户锁定风险。在本地校验不会有锁定风险或频率限制。 # 0x01 实践 ## 1 使用 net use 命令 可以通过命令行使用 net use 命令来验证本地账户的密码。打开命令提示符&#xff08;CMD&#xff0…

【设计模式】【行为型模式】观察者模式(Observer)

&#x1f44b;hi&#xff0c;我不是一名外包公司的员工&#xff0c;也不会偷吃茶水间的零食&#xff0c;我的梦想是能写高端CRUD &#x1f525; 2025本人正在沉淀中… 博客更新速度 &#x1f4eb; 欢迎V&#xff1a; flzjcsg2&#xff0c;我们共同讨论Java深渊的奥秘 &#x1f…

OSPF高级特性(3):安全特效

引言 OSPF的基础我们已经结束学习了&#xff0c;接下来我们继续学习OSPF的高级特性。为了方便大家阅读&#xff0c;我会将高级特性的几篇链接放在末尾&#xff0c;所有链接都是站内的&#xff0c;大家点击即可阅读&#xff1a; OSPF基础&#xff08;1&#xff09;&#xff1a;工…

把 DeepSeek1.5b 部署在显卡小于4G的电脑上

这里写自定义目录标题 介绍准备安装 Ollama查看CUDA需要版本安装CudaToolkit检查Cuda是否装好二、设置Ollama环境变量三、验证是否跑在GPU上ollama如何导入本地下载的模型安装及配置docker安装open-webui启动open-webui开始对话介绍 Deepseek1.5b能够运行在只用cpu和gpu内存小…

WebSocket与Socket.io的区别

文章目录 引言一、WebSocket&#xff1a;原生的实时通信协议&#xff08;一&#xff09;WebSocket 是什么&#xff08;二&#xff09;WebSocket 的工作原理&#xff08;三&#xff09;WebSocket 的使用方法&#xff08;四&#xff09;WebSocket 的优势&#xff08;五&#xff0…

STM32 裸机 C编程 vs micropython编程 vs linux python

以led点亮为例。 STM32 裸机 C编程需要设置时钟&#xff0c;管脚。 static void MX_GPIO_Init(void) {GPIO_InitTypeDef GPIO_InitStruct {0};// GPIO端口时钟使能__HAL_RCC_GPIOA_CLK_ENABLE();// 配置PA5为推挽输出模式GPIO_InitStruct.Pin GPIO_PIN_5;GPIO_InitStruct.M…

AI语言模型的技术之争:DeepSeek与ChatGPT的架构与训练揭秘

云边有个稻草人-CSDN博客 目录 第一章&#xff1a;DeepSeek与ChatGPT的基础概述 1.1 DeepSeek简介 1.2 ChatGPT简介 第二章&#xff1a;模型架构对比 2.1 Transformer架构&#xff1a;核心相似性 2.2 模型规模与参数 第三章&#xff1a;训练方法与技术 3.1 预训练与微调…

稀土抑烟剂——为汽车火灾安全增添防线

一、稀土抑烟剂的基本概念 稀土抑烟剂是一类基于稀土元素&#xff08;如稀土氧化物和稀土金属化合物&#xff09;开发的高效阻燃材料。它可以显著提高汽车内饰材料的阻燃性能&#xff0c;减少火灾发生时有毒气体和烟雾的产生。稀土抑烟剂不仅能提升火灾时的安全性&#xff0c;…

硅基流动平台大模型 DeepSeek API 调用示例

硅基流动平台大模型 API 调用示例 硅基流动平台作为一个集成多种主流开源大模型的云服务平台&#xff0c;为用户提供了便捷的 API 调用方式&#xff0c;让用户无需自建硬件或进行复杂配置&#xff0c;即可轻松使用各种大模型。以下是详细的硅基流动平台大模型 API 调用示例&am…

vue项目 Axios创建拦截器

Axios 1. Axios 和 Ajax 简介2. Axios 和 Ajax 的区别3. 从 按钮 到 Axios请求后端接口的 大致顺序 1. Axios 和 Ajax 简介 Ajax&#xff08;Asynchronous JavaScript and XML&#xff09; 不是一种技术&#xff0c;而是一个编程技术概念&#xff0c;核心是通过 XMLHttpReques…

CNN-BiGRU卷积神经网络双向门控循环单元多变量多步预测,光伏功率预测

CNN-BiGRU卷积神经网络双向门控循环单元多变量多步预测&#xff0c;光伏功率预测 代码下载&#xff1a;CNN-BiGRU卷积神经网络双向门控循环单元多变量多步预测&#xff0c;光伏功率预测 一、引言 1.1、研究背景及意义 随着全球能源危机和环境问题的日益严重&#xff0c;可再…

碳纤维复合材料制造的六西格玛管理实践:破解高端制造良率困局的实战密码

碳纤维复合材料制造的六西格玛管理实践&#xff1a;破解高端制造良率困局的实战密码 在全球碳中和与高端制造升级的双重驱动下&#xff0c;碳纤维复合材料行业正经历前爆发式增长。航空航天、新能源汽车、风电叶片等领域对碳纤维产品的性能稳定性提出近乎苛刻的要求&#xff0…

如何保证Redis和MySQL数据的一致性刨析

1、常见的缓存更新策略&#xff1a; 定义&#xff1a;主要用来进行redis和mysql的数据同步更新的一些策略 内存淘汰&#xff1a;等触发淘汰机制后&#xff0c;刚好淘汰到了用户查询的数据&#xff0c;此时是null&#xff0c;会进行查询数据库并写入到缓存中&#xff0c;此时…

Golang GORM系列:GORM 高级查询教程

有效的数据检索是任何程序功能的基础。健壮的Go对象关系映射包&#xff08;称为GORM&#xff09;除了标准的CRUD操作之外&#xff0c;还提供了复杂的查询功能。这是学习如何使用GORM进行高级查询的综合资源。我们将涵盖WHERE条件、连接、关联、预加载相关数据&#xff0c;甚至涉…

协议-LVDS

是什么&#xff1f; LVDS 全称为 Low-Voltage Differential Signaling&#xff0c;低电压差分信号 低功耗、低误码率、低串扰和低辐射的差分信号&#xff0c;采用-350mV~350mV极底的电压摆幅高速差动传输数据&#xff0c;实现点对点或一点对多点的连接 由于电压幅度低&#xf…

dma_ddr 的编写 通过mig控制ddr3

此外还有别的模块 本模块是 其中一个 timescale 1ns/1ps module dma_ctrl (input wire ui_clk , //100MHZ 用户时钟input wire ui_rst_n ,//写fifo的写端口 input wire wf_wr_clk , //由数据产生模块的时…

数据中心网络监控

数据中心是全球协作的特定设备网络&#xff0c;用来在internet网络基础设施上传递、加速、展示、计算、存储数据信息。 对于任何利用IT基础设施的企业来说&#xff0c;数据中心都是运营的核心&#xff0c;它本质上为整个业务网络托管业务应用程序和存储空间。数据中心可以是任…

w~大模型~合集30

我自己的原文哦~ https://blog.51cto.com/whaosoft/13284996 #VideoMamba 视频理解因大量时空冗余和复杂时空依赖&#xff0c;同时克服两个问题难度巨大&#xff0c;CNN 和 Transformer 及 Uniformer 都难以胜任&#xff0c;Mamba 是个好思路&#xff0c;让我们看看本文是…

将Excel中的图片保存下载并导出

目录 效果演示 注意事项 核心代码 有需要将excel中的图片解析出来保存到本地的小伙子们看过来&#xff01;&#xff01;&#xff01; 效果演示 注意事项 仅支持xlsx格式&#xff1a;此方法适用于Office 2007及以上版本的.xlsx文件&#xff0c;旧版.xls格式无法使用。 图片名…