feat(client): 实现稳定的设备ID生成与认证优化

- 重构设备ID生成逻辑,采用多重降级策略确保唯一性与稳定性- 移除客户端SQLite缓存依赖,改用localStorage存储token与设备ID
- 优化认证流程,简化token管理与会话恢复逻辑- 增加设备数量限制检查,防止超出配额
- 更新SSE连接逻辑,适配新的认证机制
- 调整Redis连接池配置,提升并发性能与稳定性
- 移除冗余的缓存接口与本地退出逻辑
- 修复设备移除时的状态处理问题,避免重复调用offline接口
- 引入OSHI库用于硬件信息采集(备用方案)- 更新开发环境API地址配置
This commit is contained in:
2025-10-13 16:12:51 +08:00
parent 4c2546733e
commit f614860eee
17 changed files with 391 additions and 324 deletions

View File

@@ -139,7 +139,26 @@ public class ClientAccountController extends BaseController {
if (!"0".equals(account.getStatus())) {
return AjaxResult.error("账号已被停用");
}
// 检查设备数量限制
String clientId = loginData.get("clientId");
if (!StringUtils.isEmpty(clientId)) {
ClientDevice currentDevice = clientDeviceMapper.selectByDeviceId(clientId);
if (currentDevice == null || "removed".equals(currentDevice.getStatus())) {
int deviceLimit = account.getDeviceLimit();
java.util.List<ClientDevice> userDevices = clientDeviceMapper.selectByUsername(username);
int activeDeviceCount = 0;
for (ClientDevice d : userDevices) {
if (!"removed".equals(d.getStatus()) && !d.getDeviceId().equals(clientId)) {
activeDeviceCount++;
}
}
if (activeDeviceCount >= deviceLimit) {
return AjaxResult.error("设备数量已达上限(" + deviceLimit + "个),请先移除其他设备");
}
}
}
String accessToken = Jwts.builder()
.setHeaderParam("kid", jwtRsaKeyService.getKeyId())
.setSubject(username)
@@ -157,6 +176,7 @@ public class ClientAccountController extends BaseController {
result.put("expireTime", account.getExpireTime());
return AjaxResult.success("登录成功", result);
}

View File

@@ -43,6 +43,27 @@ public class ClientDeviceController {
return account.getDeviceLimit();
}
/**
* 检查设备数量限制
*
* @param username 用户名
* @param currentDeviceId 当前设备ID检查时排除此设备
* @throws RuntimeException 如果设备数量已达上限
*/
private void checkDeviceLimit(String username, String currentDeviceId) {
int deviceLimit = getDeviceLimit(username);
List<ClientDevice> userDevices = clientDeviceMapper.selectByUsername(username);
int activeDeviceCount = 0;
for (ClientDevice d : userDevices) {
if (!"removed".equals(d.getStatus()) && !d.getDeviceId().equals(currentDeviceId)) {
activeDeviceCount++;
}
}
if (activeDeviceCount >= deviceLimit) {
throw new RuntimeException("设备数量已达上限(" + deviceLimit + "个),请先移除其他设备");
}
}
/**
* 查询设备配额与已使用数量
*
@@ -83,20 +104,15 @@ public class ClientDeviceController {
public AjaxResult register(@RequestBody ClientDevice device, HttpServletRequest request) {
ClientDevice exists = clientDeviceMapper.selectByDeviceId(device.getDeviceId());
String ip = IpUtils.getIpAddr(request);
// 从请求体读取用户名和操作系统,构建设备名称
String username = device.getUsername();
String os = device.getOs();
String deviceName = username + "@" + ip + " (" + os + ")";
if (exists == null) {
// 检查设备数量限制
int deviceLimit = getDeviceLimit(device.getUsername());
List<ClientDevice> userDevices = clientDeviceMapper.selectByUsername(device.getUsername());
int activeDeviceCount = 0;
for (ClientDevice d : userDevices) {
if (!"removed".equals(d.getStatus())) activeDeviceCount++;
}
if (activeDeviceCount >= deviceLimit) {
return AjaxResult.error("设备数量已达上限(" + deviceLimit + "个),请先移除其他设备");
try {
checkDeviceLimit(device.getUsername(), device.getDeviceId());
} catch (RuntimeException e) {
return AjaxResult.error(e.getMessage());
}
device.setIp(ip);
device.setStatus("online");
@@ -128,7 +144,6 @@ public class ClientDeviceController {
}
/**
* 移除设备
*
* 根据 deviceId 删除设备绑定记录。
*/
@PostMapping("/remove")
@@ -173,36 +188,42 @@ public class ClientDeviceController {
/**
* 设备心跳
* 若设备未注册则按注册逻辑插入;已注册则更新在线状态和设备信息
* 所有情况都检查设备数量限制,被移除设备允许重新注册(需检查配额)
*/
@PostMapping("/heartbeat")
public AjaxResult heartbeat(@RequestBody ClientDevice device, HttpServletRequest request) {
ClientDevice exists = clientDeviceMapper.selectByDeviceId(device.getDeviceId());
String ip = IpUtils.getIpAddr(request);
// 从请求体读取用户名和操作系统,构建设备名称
String username = device.getUsername() ;
String username = device.getUsername();
String os = device.getOs();
String deviceName = username + "@" + ip + " (" + os + ")";
// 统一检查设备数量限制
try {
checkDeviceLimit(device.getUsername(), device.getDeviceId());
} catch (RuntimeException e) {
return AjaxResult.error(e.getMessage());
}
if (exists == null) {
// 检查设备数量限制
int deviceLimit = getDeviceLimit(device.getUsername());
List<ClientDevice> userDevices = clientDeviceMapper.selectByUsername(device.getUsername());
int activeDeviceCount = 0;
for (ClientDevice d : userDevices) {
if (!"removed".equals(d.getStatus())) activeDeviceCount++;
}
if (activeDeviceCount >= deviceLimit) {
return AjaxResult.error("设备数量已达上限(" + deviceLimit + "个),请先移除其他设备");
}
// 新设备注册
device.setIp(ip);
device.setStatus("online");
device.setLastActiveAt(new java.util.Date());
device.setName(deviceName);
clientDeviceMapper.insert(device);
} else if ("removed".equals(exists.getStatus())) {
AjaxResult res = AjaxResult.error("设备已被移除");
res.put("bizCode", "DEVICE_REMOVED");
return res;
// 被移除设备重新激活
exists.setUsername(device.getUsername());
exists.setName(deviceName);
exists.setOs(device.getOs());
exists.setStatus("online");
exists.setIp(ip);
exists.setLocation(device.getLocation());
exists.setLastActiveAt(new java.util.Date());
clientDeviceMapper.updateByDeviceId(exists);
} else {
// 已存在设备更新
exists.setUsername(device.getUsername());
exists.setStatus("online");
exists.setIp(ip);