Pingnex 项目变更记录
全平台完整变更历史 · 功能 / 安全 / 性能 / 配置 全覆盖 · 不含具体代码引用
🔍
未找到匹配内容
尝试其他关键词,如"XSS"、"红包"、"CORS"
v4.1.0
2026-06-02安全加固
| 类别 | 问题描述 | 修复方向 |
|---|---|---|
| 验证码限流竞态 | 短信/邮件发送频率限制通过两步操作实现(先查询是否有锁、再写入锁),并发请求可同时通过检查,造成同一用户在 60 秒内多次触发短信发送,产生资费损耗和骚扰风险 | 改为单步原子写入操作,写入成功才允许发送,失败(已存在锁)直接拒绝,两步之间的并发窗口彻底消除 |
| 鉴权路径过宽 | 系统配置接口以通配符方式放行整个路径前缀,若未来该前缀下新增接口,会被自动纳入无鉴权范围而未被察觉 | 改为只精确放行当前已知的两个无需鉴权的公开接口(版本检查、客户端配置读取),后续新增接口默认受保护 |
| 分页无上限 | 消息删除记录查询无数量上限,长期活跃用户可能积累数万条删除记录,单次全量加载导致内存压力 | 加入单次查询最大条数限制 |
稳定性修复
| 类别 | 问题描述 | 修复方向 |
|---|---|---|
| 退款任务死循环 | 红包退款定时任务处理失败时,失败的红包状态未变更,下次轮询依然出现在查询结果中,导致任务在同一批数据上无限重复执行 | 记录本轮执行中失败的红包 ID,下次查询时将其排除,失败任务等待下次定时周期再重试而非立即循环 |
| 批量解封性能 | 用户解封定时任务对每个待解封用户单独执行一次数据库更新,百条记录产生百次数据库往返 | 收集整批用户 ID,合并为一次批量更新操作 |
| 定时任务中断 | 群封禁解除、文件清理、好友申请过期、敏感词重载等定时任务中,单条记录处理失败会抛出异常并中止整批任务,后续条目全部跳过 | 对每条记录的处理逻辑包裹独立异常捕获,单条失败只记录错误日志并跳过该条,不影响整批继续执行 |
| 全量加载 OOM 风险 | 无效文件清理任务在启动时全量加载用户头像和群头像 URL,超大规模环境下该列表可能占用大量内存 | 任务执行前先检查用户数和群组数,超过阈值时跳过本次执行并发出告警,提示需要改为流式处理后再启用 |
性能优化
| 类别 | 内容 |
|---|---|
| 评论查询上限 | 朋友圈评论列表无上限,热门动态可能积累大量评论,单次全量加载;加入最大返回条数限制,防止超大响应体 |
| 可见用户约束 | 朋友圈发布时可见用户列表无长度上限,可提交超大列表;加入最大条目约束,防止异常数据写入 |
| 类别 | 内容 |
|---|---|
| 统计查询上限 | 消息统计接口按天数查询,缺少上界约束,可传入超大天数值触发全表扫描;加入最大天数限制(365 天),并在两处重复逻辑中均修复,添加后续重构标记 |
| 开发配置凭据 | 开发配置中 MinIO 访问凭据使用弱默认值,若被误用于生产或预发布环境存在泄露风险;改为空默认值,未配置时启动失败 |
v4.0.4
2026-05-30稳定性修复
| 类别 | 问题描述 | 修复方向 |
|---|---|---|
| 定时任务异常隔离 | 群解封、用户解封、文件过期清理任务的循环体没有异常保护,处理单条记录时出现错误会导致整批任务中断 | 每条记录包裹独立错误捕获,单条失败跳过并记录日志,不影响整批执行 |
| 并行流线程安全 | 退群消息处理使用并行流,每个分支独立发起数据库查询,多线程并发争用连接池,且并行写入集合存在并发修改异常风险 | 改为串行顺序处理,消除并发竞争,性能差异在实际场景中可忽略 |
| 流资源泄漏 | 压缩流和输入流在异常中断时未被正确关闭,导致文件句柄泄漏 | 改用语言级资源自动管理,无论是否异常均确保流关闭 |
性能与缓存
| 类别 | 问题描述 | 修复方向 |
|---|---|---|
| Redis 键无过期时间 | 好友免打扰列表、群组免打扰列表、系统消息已读位置等业务键长期存在无过期时间,随时间无限增长 | 为用户级业务键设置 30 天 TTL,长期不活跃用户的数据自动清理,服务重启或重新登录时从数据库重建 |
| 分布式锁阻塞 | 分布式锁获取使用阻塞模式,当锁被其他实例持有时当前线程无限等待,在定时任务密集场景下会耗尽调度线程池 | 改为尝试获取模式,获取失败时直接跳过本次执行,下次调度周期再重试,不阻塞线程 |
| 离线消息限制 | 退群记录的历史消息查询使用写死的数量上限,与其他同类查询的常量不一致;统一提取为具名常量,消除魔法数字 |
v4.0.3
2026-05-27安全加固
| 类别 | 问题描述 | 修复方向 |
|---|---|---|
| CORS 配置失效 | 跨域配置同时启用了"允许携带凭证"和"允许全部来源通配符",在 Spring 框架新版本中这两项配置不兼容,导致带凭证的跨域请求被静默拒绝 | 改用模式匹配方式声明允许的来源,兼容新版框架行为,允许携带凭证的跨域请求正常通过 |
| IP 信任链伪造 | 客户端 IP 通过读取转发链的第一个 IP 获取,攻击者可在请求头中伪造任意 IP,绕过基于 IP 的注册频率限制 | 改为读取转发链中最靠近网络边界的最后一个 IP,该值由负载均衡器或反向代理追加,客户端无法伪造 |
| 邀请链接泄露用户 ID | 邀请注册流程中,注册失败时的跳转 URL 包含邀请人的用户 ID,该 ID 出现在浏览器地址栏、浏览器历史和服务器访问日志中,属于非必要的用户信息暴露 | 失败跳转 URL 改为只携带错误码,不包含任何用户身份信息 |
稳定性修复
| 类别 | 问题描述 | 修复方向 |
|---|---|---|
| 退款操作无事务 | 红包过期退款涉及三个写操作:更新红包状态、增加用户余额、写入账单流水,三步之间没有事务保护,中间步骤失败会造成数据状态不一致(如余额已增加但无账单记录) | 三步操作合并到同一事务中,任意一步失败全部回滚,保证资金台账一致性 |
| 类别 | 问题描述 | 修复方向 |
|---|---|---|
| RSA 私钥暴露 | 前端代码中包含 RSA 私钥,构建后私钥以明文形式内嵌于 JavaScript 包中,任何下载了前端资源的用户均可提取私钥;对应的解密函数从未被实际调用(死代码) | 完全移除私钥和解密函数,加注释说明解密操作应由服务端完成,前端只持有公钥用于加密 |
| HTML 文本提取 XSS | 将富文本内容转换为纯文本时,直接赋值给 DOM 节点的 innerHTML 属性,包含 <img onerror> 等 payload 的内容会在赋值时立即执行 | 改用浏览器安全 API 进行 HTML 解析,只提取文本内容,不触发任何脚本或事件处理器 |
v4.0.2
2026-05-24| 类别 | 问题描述 | 修复方向 |
|---|---|---|
| 主服务无跨域保护 | 主业务服务(im-platform)没有任何 CORS 配置,跨域行为完全依赖反向代理,部署配置变化时存在敞口 | 增加独立的跨域配置,默认拒绝所有跨域(fail-closed),通过环境变量注入允许的前端域名,开发环境显式放开 |
| 生产 SQL 日志泄露 | SQL 执行日志配置写在基础配置文件中,生产环境未覆盖,所有 SQL 语句(含查询参数)打印到标准输出,若日志集中采集会导致用户数据泄露 | 生产配置中显式关闭 SQL 日志输出 |
| Redis 密码配置缺失 | 开发配置中 Redis 未配置密码字段,但基础配置要求密码必填,导致未设置环境变量时开发环境启动失败 | 开发配置中将 Redis 密码设为可选空值,允许本地无密码 Redis 启动 |
| 退群消息查询无限 | 查询用户退出的群组记录时没有数量限制,频繁加退群的用户可能积累大量记录,触发无界查询 | 加入单次查询最大条数限制 |
| 类别 | 问题描述 | 修复方向 |
|---|---|---|
| 实名认证信息明文返回 | 管理后台接口返回实名认证记录时,完整身份证号和证件照片 URL 均未做任何处理,管理员账号被盗后可批量获取用户真实身份证信息 | 身份证号在返回前做脱敏处理(仅显示前3后4位),证件照片 URL 不再包含在接口响应中 |
| 生产跨域配置缺失 | 生产配置文件中没有跨域相关配置,运维人员容易遗漏注入跨域白名单环境变量,导致前端无法访问后台 API | 生产配置中明确声明跨域白名单通过环境变量注入,并加注释说明必须配置,未配置时默认拒绝所有跨域请求 |
| 应用名称硬编码 | 应用名称写死为旧品牌名,推送通知等场景显示旧品牌 | 改为通过环境变量注入,默认值更新为当前品牌名称 |
| 类别 | 内容 |
|---|---|
| 构建环境自动识别 | 开发和生产环境的 API 地址切换依赖手动修改配置文件,容易忘记或提交错误状态;改为根据构建环境变量自动判断,彻底消除人为错误 |
| 应用名称更新 | 应用图标和系统标题更新为当前品牌名称 |
| 类别 | 内容 |
|---|---|
| 自动登录空表单问题 | 重启应用后自动登录逻辑直接触发表单提交,但密码不再持久化,表单内容为空,导致一次无效的登录请求;改为检查会话令牌是否有效,有效则直接跳转主界面,无效则展示登录表单等待用户输入 |
| 生产环境开发工具 | 生产构建中调试开关未明确关闭,在部分场景下可能意外开启开发者工具 |
v4.0.1
2026-05-21安全加固
| 类别 | 问题描述 | 修复方向 |
|---|---|---|
| 消息内容日志 | 发送私聊和群聊消息时,完整消息内容被写入应用日志。若日志集中采集(如 ELK),所有用户私信对运维/安全人员完全可见,违反数据隐私基线 | 日志只记录消息类型和内容长度,不记录实际内容 |
| 证件照片接口暴露 | 实名认证接口将用户身份证正反面照片的完整访问链接返回给客户端,一旦账号令牌泄露即可访问证件照片 | 证件照片字段不再包含在接口响应中;身份证号仅返回脱敏版本 |
| 令牌验证顺序 | 身份验证拦截器先解析令牌内容再验证签名,意味着攻击者可以伪造令牌内容(如将自己的用户 ID 替换为管理员 ID)并在签名验证之前让伪造内容进入业务逻辑 | 验证顺序调整为先校验签名合法性,签名通过后再解析内容;同时修复请求结束时未清理线程本地用户状态的问题 |
| 已读回执跨群 | 标记群消息已读的接口未校验被标记消息是否属于当前群,攻击者可通过传入其他群的消息 ID 影响非本群的已读状态 | 验证消息归属,确保被操作的消息确实属于当前群组 |
| 消息删除越权 | 消息删除操作仅校验了会话 ID,未验证具体消息是否属于该会话,攻击者可将其他会话的消息 ID 混入删除请求 | 在执行删除前验证每条消息确实属于指定会话 |
| 已读推送无好友校验 | 将私聊消息标记为已读时未验证目标用户是否为好友,任意合法用户可向任意他人推送"已读"状态消息 | 操作前验证双方好友关系 |
| 群组成员空值 | 获取群成员的管理员标记时直接进行空值拆箱,若数据库中该字段为空会导致服务崩溃 | 改为空值安全的判等方式 |
| 红包参数无约束 | 发送红包的接口参数(红包类型、拆分方式、祝福语、支付密码等)未做格式和范围校验,可传入异常数据 | 各参数加入类型约束、长度限制和取值范围校验 |
性能优化
| 类别 | 内容 |
|---|---|
| 红包详情 N+1 查询 | 查看红包领取详情时,对每条领取记录单独查询用户信息,N 条记录产生 N 次查询;改为批量一次性查询所有相关用户,单次请求的数据库查询次数从 N 次降为 1 次 |
| 文件上传双次读取 | 图片上传时对同一文件流读取两次(一次计算 MD5,一次读取图片尺寸),部分环境下流不支持重复读取会导致失败;改为一次性读取到内存缓冲区,后续操作均从缓冲区读取 |
推送任务稳定性
| 类别 | 内容 |
|---|---|
| 坏任务永久阻塞队列 | 系统消息推送任务处理了消息不存在或接收者 ID 格式错误的任务时,任务状态长期停留在"等待发送"或"发送中",持续阻塞队列中正常任务的处理 |
配置
| 类别 | 内容 |
|---|---|
| 开发配置弱默认凭据 | 开发配置文件中数据库密码、MinIO 访问密钥均使用弱默认值(root、minioadmin),若误用于预发布环境存在凭据泄露风险 |
| MinIO 外部域名配置 | MinIO 外部可访问域名硬编码为内网地址,存储到数据库的文件链接使用内网地址,外部用户无法访问 |
| 类别 | 问题描述 | 修复方向 |
|---|---|---|
| 贴纸导出 SSRF | 贴纸批量导出功能接受用户提供的 URL 并直接发起 HTTP 请求,攻击者可构造内网地址进行服务端请求伪造(SSRF),探测内网服务 | 导出前验证 URL 的协议、域名、端口和路径前缀,只允许来自已配置的 MinIO 存储域名;验证内容类型为图片;加入单文件大小和总量上限,防止内存耗尽 |
| 贴纸导出资源泄漏 | 批量导出中断时,已打开的压缩流和 HTTP 连接未被正确关闭,导致资源泄漏 | 改用自动资源管理,确保任何情况下资源都被关闭 |
| 贴纸缓存失效不全 | 更新或删除贴纸时,缓存只清除当前分组的缓存,跨分组操作后其他分组的缓存仍持有旧数据 | 改为清除全部相关缓存,确保跨分组操作后数据一致 |
| Swagger 生产暴露 | Swagger UI 和 API 文档在生产配置中未明确关闭,攻击者可通过访问文档接口枚举所有 API 路径、参数和响应结构 | 生产配置中明确禁用 API 文档和 Swagger UI |
| 类别 | 问题描述 | 修复方向 |
|---|---|---|
| 注册提示 XSS | 注册流程的提示信息使用 HTML 富文本渲染模式,若服务端返回包含 HTML 标签的内容会被解析执行 | 改为纯文本渲染,提示内容不经过 HTML 解析 |
| 用户响应体 XSS | 用户详情页展示接口响应内容时使用 innerHTML 注入,若数据中包含脚本标签会被执行 | 改为通过安全的 DOM 节点构建方式展示,不经过 HTML 解析 |
| 外链协议未限制 | 链接渲染组件允许任意协议的 URL,包括 javascript: 等危险协议 | 限制只允许 http 和 https 协议,并为外链加入安全属性防止新页面访问原始页面上下文 |
| 密码落盘 | 登录页的"记住我"功能将密码持久化到本地存储,设备被他人使用时可直接获取密码 | 只持久化用户名和租户标识,密码每次重新输入 |
| 调试日志清理 | 生产代码中存在 13 处调试日志输出,其中部分打印了用户相关对象,在浏览器控制台可见 | 全部清除 |
| 类别 | 问题描述 | 修复方向 |
|---|---|---|
| 外部链接协议绕过 | 外链页面通过正则表达式检查 URL 协议,非锚定正则可被构造特殊 URL 绕过(如 javascript:void//https://x.com),导致危险协议链接被放行 | 改用 URL 解析器验证协议字段,只允许 https: 协议,精确匹配不受格式绕过 |
| 系统消息外链未校验 | 服务端下发的系统消息中包含跳转链接,客户端直接使用该链接打开内嵌浏览器,服务端如被攻击可下发恶意链接 | 客户端对链接再次进行协议校验,非 https 链接不打开内嵌浏览器 |
| 注册密码落盘 | 注册成功后的自动登录流程将密码写入本地存储 | 自动登录使用注册返回的令牌,不写入密码 |
| 调试日志清理 | 全端存在约 75 处调试日志输出,其中包含登录成功、用户信息对象、令牌刷新失败等敏感流程的日志,在真机调试控制台可见 | 全部清除 |
| 类别 | 问题描述 | 修复方向 |
|---|---|---|
| 聊天顶部 XSS | 聊天置顶消息展示时,服务端返回的 JSON 字段直接插入 HTML 渲染,若服务端数据被篡改可注入脚本 | 所有字段经 HTML 转义后再插入 |
| 链接构建 XSS | URL 通过字符串拼接构建 HTML 链接,URL 中包含引号可破坏属性边界,注入额外的 HTML 属性或脚本 | 改为对 URL 进行 HTML 属性编码后再拼接,引号等特殊字符被转义为实体 |
| 硬编码测试凭据 | 重置密码页面遗留了开发测试时写入的账号和密码默认值 | 完全移除,表单初始为空 |
| 历史密码读取 | 登录页启动时从本地存储读取历史保存的密码 key,属于已废弃的密码持久化逻辑遗留代码 | 移除该读取逻辑 |
| 外链协议未限制 | 与 admin-web 同类问题 | 同样修复为只允许 http/https 协议,加入安全属性 |
| IPC 通道对齐 | Electron 预加载脚本注册的 IPC 通道名称与主进程实际监听的通道名称不完全一致,导致部分 UI 操作无响应,也存在通道名称错位带来的安全风险 | 完整比对并对齐两侧通道名称;事件监听器在组件销毁时正确取消注册,防止内存泄漏 |
| 调试日志清理 | 存在 38 处调试日志,含 WebRTC 信令、ICE 候选信息、摄像头麦克风状态等 | 全部清除 |
v4.0.0
2026-05-19| 端 | 级别 | 内容 |
|---|---|---|
| 后端主服务 | 中 | 群成员管理员标记的空值安全判断 |
| 后端主服务 | 高危 | 系统消息推送任务状态机完整化(坏任务自动取消,防止永久占用队列) |
| 后端主服务 | 中 | 发送红包接口参数约束(类型、拆分方式、祝福语、密码长度范围) |
| 后台管理服务 | 高危 | 推送任务创建时关联消息存在性校验,接收者 ID 格式校验 |
| 仓库 | 旧版本 | 新版本 |
|---|---|---|
| 后端主服务(所有子模块) | 3.0.0 | 4.0.0 |
| 后台管理服务 | — | 4.0.0(新增版本属性) |
| 后台管理前端 | 3.0 | 4.0.0 |
| 移动端 | — | 4.0.0(新增版本字段) |
| 桌面端 | 3.10.0 | 4.0.0 |
v3.3.0
2026-05-19新增功能
| 类别 | 内容 |
|---|---|
| 合并转发展示 | 聊天记录中展示合并转发消息的摘要气泡,气泡内显示原始消息的发送者和内容预览;点击气泡可展开完整的历史聊天记录对话框 |
| 群封禁时长选择 | 执行封禁操作时支持选择封禁时长(提供若干预设时长选项和自定义输入),区分临时封禁和永久封禁 |
| 抢红包防重复提交 | 点击抢红包后按钮立即进入禁用状态,待服务端返回结果后恢复,防止网络延迟导致同一用户重复提交抢红包请求 |
v3.2.0
2026-05-18新增功能
| 类别 | 内容 |
|---|---|
| 朋友圈页面 | 展示关注用户的动态信息流,支持点赞(幂等,不可重复点赞)和评论(支持回复特定评论);发布入口支持选择图片(最多9张)、录入文字内容和设置可见范围 |
| 红包弹窗 | 抢红包时展示领取动效,领取完成后显示当前红包的领取情况(领取人、领取金额、是否手气最佳);已过期或已领完的红包展示对应状态 |
| 钱包页面 | 展示账户余额,提供充值入口(跳转支付流程)和提现申请入口;账单列表按时间倒序分页展示所有收支记录,每条记录标注来源(红包收入/提现/充值) |
| 发现页 | 聚合朋友圈、小程序、附近的人等社交功能的导航入口页,为后续功能扩展提供统一框架 |
| 合并转发展示 | 消息列表中合并转发消息以摘要气泡形式展示;点击进入完整历史记录展开页,按原始消息顺序列出全部内容 |
| 群封禁枚举对齐 | 客户端群封禁类型常量与服务端枚举完整对齐,确保封禁操作的类型标识在前后端一致 |
v3.1.0
2026-05-18新增功能
| 类别 | 内容 |
|---|---|
| 朋友圈管理页 | 列出所有用户发布的动态,支持按用户、时间筛选;管理员可对违规内容执行下架操作(下架后原发布者可见,其他用户不可见) |
| 红包管理页 | 展示平台红包发放记录,含红包类型(私聊/群聊)、金额、发放时间、领取状态(进行中/已领完/已过期);支持查看每个红包的详细领取记录 |
| 钱包管理页 | 展示用户账单明细,支持按用户 ID 查询;显示用户当前余额,提供充值和提现的操作记录审计视图 |
| 邀请码管理页 | 查看所有邀请短链的使用情况(创建时间、有效期、已注册人数、是否过期);支持手动生成新邀请链接和配置全局邀请参数(H5 地址、Web 地址、有效期时长、IP 限流间隔) |
| 系统消息管理页 | 创建和编辑系统通知,支持配置接收范围(全部用户、指定用户、指定群组);可预览消息内容,发布后通过推送任务调度发送 |
| 推送任务调度页 | 展示所有推送任务的状态(等待发送、发送中、已完成、已取消),支持查看每个任务的目标用户数、成功/失败统计;失败任务可查看取消原因 |
| 群封禁时长控制 | 封禁群成员操作新增时长选项(若干预设时长及自定义时长输入),对应后端的定时解封任务 |
| 合并转发展示 | 聊天历史查询结果中正确渲染合并转发消息,展示消息摘要,支持展开查看完整历史内容 |
| 缺失 API 补全 | 补全后台管理前端中所有之前缺少定义的接口文件:贴纸管理、钱包操作、邀请码、朋友圈、系统消息、推送任务、企业信息、实名认证等模块的 API 定义 |
v3.0.1
2026-05-17| 编号 | 级别 | 问题描述 | 修复方向 |
|---|---|---|---|
| S-04b | 高危 | 首轮修复群已读鉴权时,第一次标记已读(maxReadedId 为空)的情况未被覆盖,导致首次已读请求触发无条件全表扫描;另外消息归属校验逻辑存在分支遗漏 | 补全边界情况,消息归属校验对所有分支一致覆盖 |
| S-02b | 高危 | 消息服务(im-server)的 JWT 密钥为硬编码值,与业务服务(im-platform)已完成的环境变量化不一致,两层服务密钥不一致会导致鉴权失效 | 消息服务 JWT 密钥改为环境变量注入,与业务服务保持一致 |
| S-08b | 中 | 请求体缓存包装类的实现存在两处错误:流是否读完的判断逻辑不准确(条件取反),同步实现中流就绪状态返回了错误值;另外没有对非文件上传请求的请求体大小加以限制,超大请求体被全量缓存至内存 | 修正两处实现错误;对非文件上传的请求体加入 2MB 内存缓存上限 |
| 编号 | 级别 | 内容 |
|---|---|---|
| S-11b | 中 | 跨域配置默认值为允许所有来源,首轮修复只添加了从配置读取的逻辑,但未设置合理的默认值,未配置时仍等同于放开所有来源;改为默认空值(fail-closed),并在开发环境配置中显式放开所有来源 |
| 编号 | 级别 | 内容 |
|---|---|---|
| S-03b | 高危 | 首轮修复 Electron IPC 白名单时,部分通道名称存在错位(前端注册的名称与主进程监听的名称不同),导致功能失效;另外事件监听器在组件销毁时没有取消注册,累积导致内存泄漏 |
| 编号 | 级别 | 内容 |
|---|---|---|
| S-03c | 中 | 首轮修复中桌面端和后台已完成密码不落盘,但后台管理前端的"记住我"和移动端的注册自动登录流程仍将密码写入本地存储 |
v3.0.0
2026-05-12红包系统
| 类别 | 内容 |
|---|---|
| 私聊红包 | 支持发送定额红包(每个红包最多 2 份),支付前校验接收方为好友,需输入支付密码确认,设置总金额和份数 |
| 群聊红包 | 支持发送定额红包(每人领取相同金额)和随机红包(金额随机分配,总额固定);份数不超过群人数 |
| 红包领取 | 每人每个红包只能领取一次,超过份数后提示已领完;记录每次领取的用户和金额;计算并标记手气最佳(随机红包中领取金额最大者) |
| 红包详情 | 发送方和领取方均可查看该红包的领取详情(领取人列表、各人金额、领取时间) |
| 自动过期 | 红包在发送后 24 小时内未被全部领取则自动过期;过期后剩余金额自动退款至发送方钱包;退款操作原子执行(状态更新、余额增加、流水记录三步在同一事务中) |
| 支付密码 | 发送红包需验证支付密码;用户首次设置支付密码时生成钱包账户;支付密码独立于登录密码,加密存储 |
钱包系统
| 类别 | 内容 |
|---|---|
| 账户管理 | 每个用户对应一个钱包账户,首次操作时自动创建;并发创建场景下确保幂等,不产生重复账户 |
| 余额管理 | 余额增减通过条件 SQL 执行(只在余额足够时才执行扣减),操作结果携带影响行数,零行数表示余额不足;并发场景下余额不会出现超扣 |
| 充值 | 预留充值入口接口,供后续对接支付通道使用 |
| 提现 | 提现前校验余额充足;创建提现记录;提现操作和余额扣减在同一事务中执行 |
| 账单记录 | 所有余额变动(红包收入、红包退款、充值、提现)均生成账单流水记录,记录操作类型、金额、关联对象(红包 ID 等)和时间 |
| 账单查询 | 支持分页查询账单列表,单页上限 100 条;服务层一次性批量加载关联的红包信息、用户信息和群组信息,消除 N+1 查询 |
其他新增功能
| 类别 | 内容 |
|---|---|
| 合并转发 | 用户可选取多条聊天记录(跨消息类型),打包成一条合并转发消息发送给任意好友或群组;接收方看到带摘要的消息气泡,点击后展开全部原始消息 |
| 昵称全局唯一 | 注册和修改昵称时在全局范围内校验唯一性,拒绝重复昵称;同时支持以用户名(区别于手机号和邮箱)作为登录凭据 |
| 快议 | 群内发起快速议题,成员可对选项进行响应;适用于群内投票和意见征集等轻量决策场景 |
性能优化
| 类别 | 内容 |
|---|---|
| 历史消息游标分页 | 群聊历史消息接口从基于偏移量的分页(LIMIT offset, size)改为游标分页(id < beforeId),避免大偏移量下的全表扫描,消息量大时性能显著提升 |
| 离线消息上限 | 群聊和私聊的离线消息单次拉取均设置最大条数(3000 条),防止重度用户一次拉取海量离线消息导致内存压力 |
| 代码结构优化 | 红包事务相关的加锁和事务逻辑提取到独立的 Spring 托管对象中,消除通过反射绕过代理的方式进行自调用,确保加锁和事务注解在正确的 AOP 代理链上生效 |
安全加固(12 项)
| 编号 | 级别 | 问题描述 | 修复方向 |
|---|---|---|---|
| S-01 | 高危 | 后台管理服务配置了两个数据源,但两个数据源均指向业务数据库,导致后台操作(如封禁用户)写入业务库,同时管理数据库内容为空 | 管理数据源改为指向管理数据库,两个数据库分别通过独立环境变量注入连接凭据 |
| S-02 | 高危 | 代码仓库中包含多处明文密钥:JWT 签名密钥、Redis 访问密码、MinIO 凭据、第三方推送平台凭据、短信服务密钥、邮件服务密钥、WebRTC TURN 服务凭据 | 全部改为通过环境变量注入,仓库中不保存任何实际密钥值;生产部署时缺少任何一个必要变量,应用启动即失败 |
| S-03 | 高危 | Electron 应用在非开发环境下仍开启了 Web 安全检查豁免和开发者工具;渲染进程的 Node.js 访问权限和上下文隔离配置不严格;IPC 通道缺少白名单管理;登录页将密码明文存储到本地存储 | 安全边界:仅在开发模式启用安全豁免和开发工具;渲染进程完全隔离,禁止 Node.js 访问;IPC 通道建立白名单;移除所有密码持久化逻辑 |
| S-04 | 高危 | 群已读接口未校验请求方是否为该群成员,任意登录用户可伪造已读位置;查看已读成员列表时未验证消息是否属于当前群,可通过其他群的消息 ID 越权读取已读用户信息 | 操作前验证请求方群成员身份;查询时验证消息与群的归属关系 |
| S-05 | 高危 | 身份验证拦截器先解析令牌内容后验证签名,伪造令牌的内容在签名被拒绝前已进入业务层(如 IP 地址等信息);请求结束后未清理线程本地用户状态,存在状态污染风险 | 验证顺序调整为先签名验证再内容解析;请求结束钩子中清理线程本地状态 |
| S-06 | 中高 | 群消息发送时,对群成员列表的判断在使用管理员标记之前,若成员数据为空(调用者不是群成员)会直接触发空指针异常 | 非空判断提前到使用字段之前,非成员调用返回业务异常而非 500 错误 |
| S-07 | 中 | 主配置文件中 Spring 根节点重复出现,YAML 规范中后者会覆盖前者,导致前一个节点下的配置项被静默丢弃,引发难以排查的配置失效问题 | 合并为单一根节点 |
| S-08 | 中 | 请求体缓存拦截器和 XSS 过滤拦截器对文件上传请求也执行了全量请求体缓存,大文件上传时整个请求体被加载到内存,存在内存耗尽风险 | 两个拦截器均对文件上传类型的请求跳过缓存处理 |
| S-09 | 中 | 私聊离线消息拉取没有数量上限,长期未登录的活跃用户重新上线时可能一次性拉取数万条消息,导致单次请求内存占用过高 | 加入最大拉取条数限制 |
| S-10 | 中 | 钱包账单查询接口未限制分页大小,可请求返回全量数据;服务层对每条账单记录逐条查询关联的红包和用户信息,产生大量数据库往返 | 分页大小加入上限约束;服务层改为批量查询所有关联信息后组装 |
| S-11 | 中 | 跨域允许来源和 WebSocket 允许来源均硬编码为允许所有来源(*),生产环境应只允许特定域名 | 两者均改为从配置读取,支持通过环境变量注入;生产部署时应配置为具体域名 |
| S-12 | 中 | 红包加锁和事务注解标注在同一个类的方法上,通过类内部直接调用时注解不生效(Spring AOP 代理机制限制),导致并发场景下锁和事务均可能失效 | 将需要加锁和事务的方法提取到独立的 Spring 托管对象,通过 Bean 调用确保代理生效 |
测试覆盖
新增 3 个测试类,共 36 个单元测试(全部通过):
| 测试模块 | 数量 | 主要覆盖场景 |
|---|---|---|
| 邀请码流程 | 10 | 参数格式校验(空链接、过长内容)、IP 访问频率超限、短链 XSS 注入防护、登录令牌不出现在重定向 URL、一次性登录码使用后失效 |
| 朋友圈服务 | 13 | 可见用户 ID 包含非法值(负数/零/null)被拒绝、过期时间计算(天数转换)、点赞幂等(重复点赞不累加)、非发布者删除被拒绝、管理员删除任意内容 |
| 红包服务 | 13 | 领取已过期/已领完/金额耗尽的红包被拒绝、钱包余额不足时发送被拒绝、并发创建钱包只产生一条记录、支付密码错误被拒绝、红包份数超过群人数被拒绝 |
破坏性变更
- 群聊历史消息接口的分页方式已变更:page 参数(页码偏移)不再支持,必须改用 beforeId 参数(游标 ID),传入上次获取到的最后一条消息 ID 来拉取更早的消息
v2.9.2
2026-05-11新增功能
| 类别 | 内容 |
|---|---|
| 合并转发消息 | 支持将多条消息(可跨不同消息类型)选中后打包成一条合并消息发出;接收方看到包含发送者摘要的消息气泡;点击气泡后展开显示完整的原始消息列表,按原始时间顺序排列 |
| 合并转发内容安全 | 打包时对所有原始消息内容经过敏感词过滤和 XSS 转义处理;被撤回的消息在展开视图中显示已撤回状态 |
| 昵称全局唯一 | 注册时检查昵称在全平台范围内是否被占用,已占用时给出提示并要求更换;修改昵称时同样进行唯一性检查,但本人当前昵称不触发冲突 |
| 用户名登录 | 注册时可自定义用户名(独立于手机号和邮箱),登录时支持三种账号类型自动识别,系统根据账号格式(手机号格式、邮箱格式、其他)判断查询方式 |
| 快议功能 | 群内成员可发起快速议题,议题包含标题和若干选项;群成员可对选项进行投票响应;适用于群内决策和意见收集等场景 |
新增功能
- 合并转发消息在聊天记录中以摘要气泡形式展示,点击后展开完整历史内容(两端样式分别适配桌面端对话框和移动端全屏页面)
v2.9.1
2026-05-10新增功能
发布与内容
| 类别 | 内容 |
|---|---|
| 图文发布 | 支持发布包含文字(最多 5000 字符)、图片(最多 9 张)和视频(最多 1 个)的动态;内容经过敏感词过滤 |
| 自动过期 | 可设置动态有效天数(visibleDays),到期后对所有用户(包括自己)均不可见;到期判断由数据库计算,不依赖定时任务 |
| 内容删除 | 发布者可随时删除自己的动态;管理员可删除任意用户的动态(用于违规内容处理);删除采用软删除,历史记录保留 |
可见性控制
| 可见类型 | 说明 |
|---|---|
| 全员可见 | 所有人均可看到此动态(受好友时间范围和陌生人设置约束) |
| 仅自己可见 | 只有发布者本人可见 |
| 指定人可见 | 只有发布者和指定的特定用户列表可见(可指定最多 500 人) |
好友与陌生人访问控制
| 设置项 | 说明 |
|---|---|
| 好友可看天数 | 用户可设置好友最多能看自己几天内的动态(如设为 180 天,好友只能看最近 180 天的动态) |
| 陌生人访问控制 | 用户可设置是否允许非好友查看自己的动态;允许时陌生人最多看最近 10 条 |
互动功能
| 类别 | 内容 |
|---|---|
| 点赞 | 点赞幂等,同一用户对同一动态只记录一次;可取消点赞 |
| 评论 | 支持对动态发表评论;支持回复特定评论(二级评论);评论内容经过敏感词过滤 |
| 评论查询 | 获取动态评论列表时,批量加载所有评论作者信息,不产生 N+1 查询 |
数据访问安全
| 类别 | 内容 |
|---|---|
| SQL 层可见性过滤 | 动态列表查询在 SQL 层完成可见性过滤,非可见内容不从数据库取出,不依赖应用层过滤(应用层过滤容易出现分页不准确问题) |
| 敏感内容管理 | 管理员查询不受可见性限制,可看到所有动态,便于内容审核 |
v2.9.0
2026-05-09新增功能
邀请短链
| 类别 | 内容 |
|---|---|
| 短链生成 | 每位用户可生成个人专属邀请短链,链接可分享到社交媒体;短链包含有效期,过期后访问跳转到过期提示页 |
| 链接配置 | 运营后台可配置邀请链接的有效时长、IP 访问频率限制间隔,以及移动端和 Web 端的注册成功跳转地址 |
| 访问限流 | 同一 IP 在配置的时间间隔内只能触发一次有效访问,防止同 IP 批量创建账号;超频访问返回频率限制提示 |
注册与登录流程
| 类别 | 内容 |
|---|---|
| 自动注册 | 访客通过邀请链接访问时,系统自动完成账号注册(使用随机生成的账号信息);注册完成后生成一次性登录码(UUID,存储在 Redis 中,5 分钟过期) |
| 免密登录 | 前端凭一次性登录码换取 JWT 访问令牌,完成登录;登录码使用后立即失效,不可复用 |
| 自动加好友 | 注册登录成功后,系统自动将新用户与邀请人建立双向好友关系;加好友操作异步执行,通过独立线程池处理,失败时自动重试,不阻塞注册响应 |
| 双端路由 | 服务端根据请求的 User-Agent 判断访问者使用的设备类型(移动端/桌面端),分别跳转到对应的注册成功落地页;落地页地址通过配置管理,无需修改代码即可调整 |
安全措施
| 类别 | 内容 |
|---|---|
| 页面 XSS 防护 | 邀请页面中的用户输入内容(如 URL 参数中的名称)经过 HTML 实体转义后输出,防止构造含脚本的邀请链接攻击访问者 |
| 一次性登录码 | 登录码只能使用一次,使用后立即从 Redis 删除,即使码被截获也无法二次利用 |
| 参数校验 | 所有邀请相关接口对入参进行格式和范围校验,非法参数直接拒绝 |
v2.8.0
2026-05-08i18n 全量覆盖
将所有服务层中剩余的硬编码中文错误提示和通知文本全部替换为 i18n 消息 key:
| 覆盖范围 | 具体内容 |
|---|---|
| 群组服务 | 群不存在、非群成员、无管理权限、成员上限、群已解散等提示 |
| 实时通话通知 | 群通话和私聊通话的来电通知、挂断通知、超时通知文本 |
| 用户服务 | 账号不存在、密码错误、账号封禁、手机号/邮箱已注册、验证码错误等提示 |
| 文件服务 | 文件类型不支持、文件超限、上传失败等提示 |
| 消息服务 | 消息内容超长、消息类型非法、发送频率过高等提示 |
安全修复
| 类别 | 级别 | 问题描述 | 修复方向 |
|---|---|---|---|
| 引用消息越权 | 高危 | 私聊和群聊中引用消息时,通过消息 ID 拉取被引用消息内容的接口没有校验消息是否属于当前用户,攻击者可枚举 ID 读取任意用户的消息内容 | 被引用消息查询前验证消息归属(私聊:发送方或接收方为当前用户;群聊:消息属于当前用户所在群) |
| 生产配置加固 | 中 | 生产配置文件中 Swagger 接口文档未关闭,且数据库密码仍使用明文值 | 关闭 Swagger;数据库密码改为环境变量注入 |
| 红包详情鉴权 | 中 | 查看红包领取详情的接口对调用方身份无限制,非参与方(非发送方也非领取方)可查看红包详情 | 接口前置鉴权,只允许红包发送方和参与领取方查看 |
| 钱包并发初始化 | 中 | 用户首次操作钱包时,多个并发请求可能同时检测到账户不存在并尝试创建,产生重复账户或数据库唯一约束异常 | 捕获唯一约束违反异常,改为执行更新操作,确保最终只有一条有效账户记录 |
| 邀请监听线程隔离 | 中 | 邀请注册的好友自动添加监听器在重试失败时执行 sleep 等待,占用公共异步线程池,影响其他异步任务的处理 | 邀请注册的好友添加逻辑迁移到独立的专用线程池,隔离重试等待对公共线程池的影响 |
| 软删除字段类型 | 中 | 朋友圈的删除标记字段使用 Java Boolean 类型,与数据库 TINYINT 类型映射时,未初始化的值为 null 而非 false,导致查询条件与预期不符,已删除内容可能出现在列表中 | 改为 Integer 类型并启用 MyBatis-Plus 软删除过滤功能,由框架统一处理软删除查询条件 |
| 并发启动竞态 | 低 | 多实例部署时,各实例在启动时均会检查并创建邀请默认配置,多个实例同时执行会产生数据库唯一约束冲突异常,导致部分实例启动失败 | 捕获唯一约束违反,将其视为正常的并发竞争结果,不中断启动流程 |
| 重复字段注入 | 低 | 群组服务层存在同一服务接口被注入两次(字段名不同),产生歧义且存在潜在的使用混乱 | 保留正确的字段,移除重复注入 |
v2.7.0
2026-05-06新增功能与优化
| 类别 | 内容 |
|---|---|
| 注册 IP 限流 | 每个 IP 地址在配置的时间窗口内只能完成有限次注册操作;计数通过 Redis 维护,利用 TTL 实现时间窗口自动滑动;超频时返回 IP 注册过于频繁的提示,防止同 IP 批量创建账号 |
| 推送活跃用户过滤 | 系统消息推送任务从全量扫描所有用户改为只查询最近指定天数内有登录行为的活跃用户,大幅降低非活跃用户群体对推送任务的影响,减少无效推送请求 |
| 消息内容校验 | 发送消息时增加内容长度上限校验,超长内容直接拒绝;增加消息类型合法性校验,非系统已定义的消息类型被拒绝,防止客户端发送未知类型消息绕过业务逻辑 |
| 退群记录时间窗口 | 查询用户退出的群组记录时,改为支持按时间范围过滤,只返回指定时间窗口内的退群记录,而非全量历史记录,减少不必要的数据传输 |
缺陷修复
| 类别 | 内容 |
|---|---|
| 合并转发枚举缺失 | 消息类型枚举中合并转发类型(值为 8)在代码中定义缺失,导致处理该类型消息时触发空指针异常;补全枚举值定义 |
数据库变更
- 用户表新增最后登录 IP 字段的索引,支持按 IP 范围查询审计和限流统计
v2.6.0
2026-05-05新增功能
用户解封
| 类别 | 内容 |
|---|---|
| 定时扫描 | 每小时执行一次,查询解封时间已到达的被封禁用户,批量执行解封 |
| 解封操作 | 清除封禁状态、清除解封时间、清除封禁原因 |
| 解封通知 | 向被解封用户通过系统消息推送解封通知,告知账号已恢复正常 |
群成员解封(解除禁言)
| 类别 | 内容 |
|---|---|
| 定时扫描 | 每小时执行一次,查询禁言时间已到达的群成员,批量执行解除禁言 |
| 解封操作 | 清除禁言状态和到期时间,成员恢复在群内正常发言权限 |
数据库变更
- 用户表新增解封时间字段(值为 null 表示永久封禁,有值表示临时封禁到指定时间)
- 群成员表新增解封时间字段(值为 null 表示永久禁言,有值表示临时禁言到指定时间)
v2.5.0
2026-05-03新增功能
群组二维码与名片安全
| 类别 | 内容 |
|---|---|
| 签名令牌 | 群二维码和群名片在生成时携带服务端签名的令牌(令牌包含群 ID 和有效期,由服务端密钥签名) |
| 令牌校验 | 通过二维码或名片申请入群时,服务端验证令牌的签名是否合法、是否在有效期内;令牌校验失败的加群请求被直接拒绝 |
| 防伪造 | 攻击者无法伪造群二维码或名片邀请(伪造的令牌无法通过签名验证),防止未授权的批量加群操作 |
WebRTC 设备同步
| 类别 | 内容 |
|---|---|
| 设备参数 | 发起私聊通话时可携带设备类型参数(移动端 / Web 端 / 桌面端),指定通话请求希望送达的终端类型 |
| 多端同步 | 被叫方多端在线时,根据发起方指定的设备类型路由通话请求,仅向目标终端推送来电通知 |
| 通话失败原因 | 定义了标准化的通话失败原因枚举(对方忙碌、拒绝接听、超时未接、设备不在线等),便于客户端展示准确的失败提示 |
v2.4.0
2026-05-01新增功能
| 类别 | 内容 |
|---|---|
| 好友申请每日限额 | 每位用户每天最多发出 50 条好友申请;计数存储在 Redis 中,每日零点(精确到次日零点时刻)自动重置;超额后当天不允许再发送新的好友申请,防止骚扰性群发申请 |
| 打招呼消息 | 好友申请被对方通过时,申请时附带的打招呼内容自动作为第一条私聊消息发送,降低新好友建立联系的社交门槛 |
| 演示账号保护 | 演示账号(由运营配置)的所有写操作被拦截,返回"演示账号不允许修改"提示;防止演示环境数据在体验演示时被破坏 |
优化改进
| 类别 | 内容 |
|---|---|
| 退出成员过滤 | 群成员管理相关的批量操作(如通知所有成员)统一在查询时过滤掉已退出的成员,避免对已离群用户发送无效操作和消息 |
| 被撤回引用消息 | 当引用回复的原始消息被撤回后,引用内容中显示的原消息文本置为空,客户端展示为"消息已被撤回",不再残留原始内容 |
v2.3.0
2026-04-29新增功能
消息删除(两种模式)
| 模式 | 说明 |
|---|---|
| 仅对我删除 | 标记指定消息对当前用户不可见,其他参与者不受影响,也不会收到任何通知;仅记录该用户对该消息的删除操作,下次拉取消息时过滤掉已标记删除的消息 |
| 对所有人撤回 | 向会话中所有在线成员推送"消息已被撤回"的系统通知,所有端更新消息气泡展示为撤回状态;离线成员下次上线时拉取消息也不再看到原消息内容 |
| 批量按会话删除 | 支持一次性标记删除某个会话中所有消息(清空聊天记录),生成一条时间戳记录,该时间戳之前的消息对该用户均不可见 |
| 删除记录存储 | 所有删除操作存储到独立的删除记录表,记录操作用户、消息 ID、会话信息、删除类型和操作时间;离线补偿时以删除记录过滤消息列表 |
自定义表情收藏
| 类别 | 内容 |
|---|---|
| 收藏与移除 | 用户可将聊天中看到的任意图片消息保存为自定义表情;可在表情面板中删除已收藏的表情 |
| 收藏上限 | 每用户最多收藏 50 个自定义表情,超过上限时需先删除旧表情才能添加新的 |
| 跨端同步 | 收藏数据存储在服务端,多端登录时表情库保持同步 |
数据库变更
- 新增消息删除记录表(记录谁、删除了哪条消息、删除类型、所在会话、操作时间)
- 新增用户自定义表情表(记录用户 ID、表情图片地址、收藏时间)
v2.2.0
2026-04-27新增功能
Caffeine 本地缓存
| 类别 | 内容 |
|---|---|
| 热点数据缓存 | 对好友列表、群成员信息、用户基本信息等频繁读取的热点数据,在应用进程内维护本地缓存,减少 Redis 网络访问 |
| 缓存一致性 | 写操作(如修改用户信息、更新好友关系)同步清除对应的本地缓存 key,确保下次读取时从 Redis 或数据库获取最新数据 |
| 分层缓存 | 本地缓存命中后直接返回,未命中时回退到 Redis 缓存,Redis 未命中时回退到数据库查询;适合读多写少的数据 |
离线推送本地化
| 类别 | 内容 |
|---|---|
| 多语言通知 | 离线推送通知的标题和正文根据用户的语言设置选择对应语言版本(中文/英文) |
| 语言偏好 | 用户可在个人设置中选择通知语言,服务端记录用户语言偏好并在推送时使用 |
| 默认语言 | 未设置语言偏好的用户使用中文通知模板 |
v2.1.0
2026-04-25新增功能
国际化(i18n)基础设施
| 类别 | 内容 |
|---|---|
| 消息资源文件 | 引入中文和英文两套消息资源文件,共收录 216 条错误码和提示消息 |
| 消息工具类 | 封装统一的消息获取工具,支持携带参数的动态消息(如"用户 {name} 不存在"中的用户名替换) |
| 群操作提示构建器 | 集中管理入群、退群、踢人、解散群等操作产生的群内系统提示文案,支持国际化 |
| 全局异常处理集成 | 全局异常处理器从 i18n 资源文件解析错误消息后返回客户端,不再直接在代码中写中文错误描述 |
通用工具类
| 类别 | 内容 |
|---|---|
| 正则常量集合 | 整理常用数据格式的正则表达式(手机号、邮箱地址、URL、身份证号、银行卡号等),集中维护,避免各处重复定义和不一致 |
| 数据脱敏工具 | 提供统一的脱敏处理方法(手机号中间位替换、邮箱名遮罩、姓名中字遮罩、身份证号中间位替换、银行卡号尾部展示等),用于日志输出和非必要的数据展示场景 |
v2.0.0
2026-04-23新增功能
强制下线机制
| 类别 | 内容 |
|---|---|
| 下线信息模型 | 强制下线携带结构化的下线原因,区分不同场景(新设备登录挤占旧设备、管理员手动下线、令牌失效、异常检测触发等),客户端根据原因展示不同提示 |
| 服务端主动断开 | WebSocket 服务在接收到强制下线指令后,主动向目标连接推送下线通知消息,客户端收到后执行本地登出清理;推送消息后服务端主动断开 WebSocket 连接 |
| 异步队列 | 强制下线指令通过 Redis 队列异步传递,业务服务无需直接调用 WebSocket 服务,保持服务解耦;定时任务负责消费队列并执行断开操作,支持失败重试 |
| 多场景下线 | 支持对指定用户的所有在线设备同时下线,或仅下线特定终端类型的设备 |
在线路由收紧
| 类别 | 内容 |
|---|---|
| 发送前在线校验 | 发送消息前统一查询目标用户当前在线状态,若目标已离线,消息不写入实时推送队列(写入离线消息队列或直接走推送通道),避免无效的实时推送操作 |
| 事件路由统一 | 用户上线和下线事件的路由逻辑统一到单一入口,消除各处不一致的在线状态判断,确保全局在线状态始终一致 |
| 握手前隔离 | WebSocket 连接在完成身份认证握手之前,不参与任何消息路由和在线状态计算,防止未认证连接污染在线状态 |
稳定性修复
| 类别 | 内容 |
|---|---|
| Redis 集群兼容 | 消息服务在启动时读取 Redis 服务信息以检测版本,但 Redis Cluster 模式下该命令返回结构与单节点不同,导致版本解析出现空值引发启动异常;改为对解析结果进行空值安全处理 |
测试
- 新增在线路由一致性测试:覆盖多端并发在线、离线转换、握手期间隔离等场景
- 新增强制下线处理器测试:覆盖下线指令接收、消息推送、连接断开基本流程
v1.0.0
2026-04-17Pingnex 是一套面向企业/社区场景的即时通讯(IM)平台,基于 box-im 开源项目二次开发,提供完整的消息、通话、社交和管理能力。
后端主服务(server)
| 层次 | 选型 | 说明 |
|---|---|---|
| Web 框架 | Spring Boot 3.3.1 | 基于 Spring 6.1,支持 Jakarta EE 10,可升级 JDK 21 使用虚拟线程 |
| 持久层 | MyBatis-Plus 3.5.7 | Lambda 条件查询、内置分页插件、乐观锁、软删除 |
| 数据库 | MySQL 8.0 | 主业务数据库(im_platform),InnoDB 引擎 |
| 缓存 | Redis 6.2 | 会话存储、在线状态、消息路由队列、分布式计数 |
| 文件存储 | MinIO | 图片/视频/文件对象存储,分桶(图片桶/视频桶/文件桶)管理 |
| 网络层 | Netty 4.x | 高性能 WebSocket 长连接服务器(im-server 模块),基于 NIO 事件驱动 |
| 分布式锁 | Redisson | 基于 Redis 的可重入锁,用于防并发竞态的关键业务操作 |
| 认证方式 | JWT(自签发) | 无状态令牌,业务服务和消息服务共享密钥,令牌携带用户标识和终端类型 |
| 密码安全 | BCrypt(强度=10) | 不可逆单向哈希,防彩虹表暴力破解,密码永不以明文存储 |
| 运行时 | JDK 17 | LTS 版本,支持 Record、Sealed Class、Text Block 等现代特性 |
后台管理服务(admin-server)
| 层次 | 选型 |
|---|---|
| 基础框架 | RuoYi-Vue-Plus 5.2.3(基于 Spring Boot 3.2.11) |
| 认证授权 | Sa-Token 1.39.0(JWT 无状态模式) |
| 多数据源 | Dynamic-Datasource(管理库 im_admin + 业务库 im_platform 动态切换) |
| 缓存/锁 | Redisson |
前端各端
| 端 | 框架与工具链 |
|---|---|
| 后台管理前端(admin-web) | Vue 3 + Vite 5 + TypeScript + Element Plus + Pinia + Axios |
| 移动端(client-uniapp) | uni-app(Vue 3 语法)+ Pinia,编译目标:App(Android/iOS)/H5/微信小程序 |
| 桌面端(desktop) | Vue 2 + Vue CLI + Electron 18 + Element UI,支持 Web 浏览器和 Electron 桌面双模式运行 |
消息能力
消息类型
| 类型 | 说明 |
|---|---|
| 文本消息 | 纯文字,支持 Emoji 字符,服务端做敏感词过滤 |
| 图片消息 | 上传时生成压缩缩略图存储,消息中携带原图和缩略图两个链接 |
| 视频消息 | 上传视频后自动提取首帧作为封面图,消息中携带视频链接和封面图链接 |
| 文件消息 | 支持任意格式文件,消息中携带文件名、大小和下载链接 |
| 语音片段 | 录制的音频片段,携带时长信息 |
| 表情/贴纸 | 系统贴纸(按分组管理)和动图表情,携带资源 URL |
| 代码块 | 代码内容 + 语言标识,客户端做语法高亮展示 |
| 位置消息 | 地理坐标 + 地址描述(地址由客户端解析) |
消息功能
| 功能 | 说明 |
|---|---|
| 引用回复 | 引用某条历史消息进行回复,被引用消息内容内嵌在新消息气泡中展示 |
| @提及 | 群聊中 @ 指定成员,被 @ 成员的未读提示有额外标记 |
| 消息撤回 | 发送后 2 分钟内可撤回,所有端同步更新为"消息已撤回"状态 |
| 消息状态 | 发送中 → 已发送 → 已送达(对方在线)→ 已读(私聊);群聊显示已读人数 |
| 离线消息 | 私聊离线消息存入 Redis 队列,重新上线后自动拉取;群聊离线消息从数据库增量拉取(基于消息 ID 游标) |
| 敏感词过滤 | 基于 DFA(确定有限自动机)算法,消息发送时实时过滤,命中则替换为 * 或拒绝发送;管理后台可动态更新词库 |
用户能力
账户管理
| 功能 | 说明 |
|---|---|
| 多方式注册 | 支持手机号(短信验证码)、邮箱(邮件验证码)、用户名三种注册方式,可通过后台配置开关各方式 |
| 验证码机制 | 图形验证码(数学题/字符)+ 短信/邮件验证码双重校验,防机器人注册 |
| 多端登录 | 同一账号可在手机、Web、桌面端同时在线;后台可配置是否允许多端,不允许时新登录自动挤占旧登录 |
| 会话管理 | JWT 令牌携带终端类型(App/Web/Desktop),同一账号不同终端独立会话,互不干扰 |
个人资料
| 功能 | 说明 |
|---|---|
| 头像 | 上传头像后存储至 MinIO,自动生成缩略图;支持删除头像恢复默认 |
| 基本信息 | 昵称、个性签名、性别、出生日期;后续版本加入全局唯一性约束 |
| 在线状态 | 实时在线/离线状态;支持隐身模式(对好友显示为离线,但实际在线) |
| 个人二维码 | 生成含用户标识的个人二维码,他人扫码后直接跳转到与该用户的私聊界面 |
安全管理
| 功能 | 说明 |
|---|---|
| 密码修改 | 需要验证旧密码后才能修改;密码加密存储,服务端无法查看明文密码 |
| 账号安全 | 登录错误次数超限后临时锁定账号(锁定时长可配置);支持通过邮箱/手机找回密码 |
好友能力
| 功能 | 说明 |
|---|---|
| 好友搜索 | 通过手机号、邮箱、用户名精确搜索用户,支持模糊搜索昵称 |
| 好友申请 | 发送申请时可附带验证信息;接收方可同意、拒绝或忽略;申请有状态流转(待处理/已同意/已拒绝/已忽略/已过期) |
| 好友列表 | 展示好友列表及实时在线状态;支持按在线状态和昵称排序 |
| 好友备注 | 为好友设置本地备注名,备注名只对自己可见 |
| 会话置顶 | 将指定好友的会话置顶到会话列表最上方 |
| 免打扰(DND) | 开启后该好友的消息不触发推送通知;DND 状态存储在 Redis 中(免打扰列表) |
| 删除好友 | 删除后双方互相从好友列表移除,历史消息保留 |
群组能力
群组管理
| 功能 | 说明 |
|---|---|
| 创建群组 | 创建者自动成为群主,可同时邀请若干好友加入;群信息包含群名称、群头像、群公告 |
| 解散群组 | 只有群主可以解散群,解散时向所有成员推送群已解散通知,群内所有消息记录保留 |
| 群信息修改 | 群主和管理员可修改群名称、群头像和群公告;公告修改后向所有成员推送通知 |
| 置顶消息 | 管理员可设置群内置顶消息,所有成员进入群聊时可看到当前置顶内容 |
成员管理
| 功能 | 说明 |
|---|---|
| 邀请成员 | 群主和管理员可邀请好友加入群组;也可通过群二维码或群名片邀请 |
| 踢出成员 | 群主可踢出任意成员;管理员只能踢出普通成员 |
| 退群 | 普通成员可主动退群;群主退群前需先转让群主 |
| 设置管理员 | 群主可将普通成员设置为管理员,也可撤销管理员权限 |
| 转让群主 | 群主可将群主身份转让给任意成员 |
消息控制
| 功能 | 说明 |
|---|---|
| 全员禁言 | 管理员开启全员禁言后,只有群主和管理员可以发言 |
| 指定成员禁言 | 对特定成员设置禁言,支持永久禁言和临时禁言(后续版本引入到期自动解禁) |
入群方式
| 方式 | 说明 |
|---|---|
| 直接邀请 | 群成员直接在界面上邀请好友加入 |
| 群二维码 | 生成群的二维码,扫码后申请加入(后续版本引入签名令牌防伪) |
| 群名片 | 将群组信息分享给他人,他人通过名片申请加入 |
音视频通话(WebRTC)
私聊通话
| 功能 | 说明 |
|---|---|
| 音频通话 | 1:1 语音通话,通过 im-platform 中转 ICE 候选和 SDP 协商,建立端到端 WebRTC 连接 |
| 视频通话 | 1:1 视频通话,同音频通话流程,附加视频流 |
| 通话状态 | 发起方等待接听、接收方来电提示、接听/拒接/挂断操作、通话时长计时 |
群聊通话
| 功能 | 说明 |
|---|---|
| 多人视频 | 群内发起多人视频通话,最多支持 webrtc.max-channel 路(默认 9 路,可配置) |
| 成员管理 | 通话中可邀请群内其他成员加入,也可移除已加入的成员 |
穿透支持
| 功能 | 说明 |
|---|---|
| STUN 服务 | 默认使用 Google 公共 STUN 服务器(stun:stun.l.google.com:19302)进行 NAT 穿透 |
| TURN 服务 | 支持配置私有 TURN 服务器凭据(用户名 + 密码),用于无法直连的复杂网络环境 |
通知与推送
| 功能 | 说明 |
|---|---|
| 实时 WebSocket 推送 | 用户在线时,好友申请、群邀请、管理员操作、系统通知等通过 WebSocket 实时推送 |
| UniPush 离线推送 | 集成个推 UniPush 平台,用户离线时通过手机厂商推送通道(APNs/FCM/各厂商通道)发送系统通知 |
| 推送内容 | 通知携带消息类型标识,客户端根据类型决定点击后的跳转行为 |
管理后台能力
| 功能 | 说明 |
|---|---|
| 用户管理 | 查询用户信息、修改账号状态(封禁/解封)、重置密码、查看登录记录 |
| 群组管理 | 查看群组信息、成员列表、消息记录;强制解散违规群组 |
| 黑名单管理 | 配置 IP 黑名单,被封禁 IP 无法注册和登录 |
| 敏感词管理 | 在线编辑敏感词库,保存后实时生效(无需重启) |
| 系统配置 | 配置注册方式开关、各功能开关、第三方服务参数 |
| 数据统计 | 用户注册趋势、消息量统计、活跃用户统计 |
服务拓扑
客户端(移动端 / 桌面端 / Web)
│
├── HTTP REST 请求 ──→ im-platform(:8888)业务服务
│ │
│ im-client(消息路由库)
│ │
│ Redis 消息队列
│ │
└── WebSocket 长连接 ──→ im-server(:8878)连接服务
│
im-common(共享模型)
im-client(消费路由结果)
管理端(admin-web)
└── HTTP REST 请求 ──→ admin-server(:8889)管理服务
├── im_admin 数据库(管理数据)
└── im_platform 数据库(只读业务数据)
并发处理机制
| 机制 | 说明 |
|---|---|
| 网络 I/O | im-server 基于 Netty NIO 事件驱动模型,少量 I/O 线程管理大量并发 WebSocket 连接,连接处理非阻塞 |
| 消息路由 | im-platform 向 Redis List 写入消息(生产者),im-server 通过定时拉取消费消息并推送到对应连接(消费者),两个服务解耦,支持水平扩展 |
| 多实例支持 | 多个 im-platform 实例通过 Redis 共享状态(用户在线状态、会话信息),消息无论到达哪个实例都能正确路由 |
| 会话状态 | 用户会话以用户 ID + 终端类型为 key 存储在 Redis,同一用户不同终端独立存储,互不干扰 |
| 分布式锁 | 关键业务操作(如好友关系建立、红包领取、余额扣减)通过 Redisson 分布式锁保证多实例下的操作串行化 |
| 数据库并发 | 使用条件 UPDATE(更新时检查前置条件)实现乐观并发控制,避免使用数据库行锁;余额扣减类操作使用 CAS(Compare And Set)SQL 模式 |
消息可靠性
| 机制 | 说明 |
|---|---|
| 离线消息存储 | 目标用户离线时,私聊消息存入持久化 Redis 队列,群聊消息写入数据库;用户上线后自动补拉 |
| 消息顺序 | 消息以数据库自增 ID 排序,保证每个会话内消息的全局有序性 |
| 状态回调 | 消息送达和已读状态通过 WebSocket 推送回发送方,发送方可感知消息到达状态 |
v1.0.0 建立了基础安全层,为后续版本的加固提供起点:
认证与授权
| 措施 | 说明 |
|---|---|
| HTTP 接口认证 | 除注册、登录、验证码等公开接口外,所有 HTTP 接口必须携带有效 JWT 令牌;拦截器在处理业务逻辑前完成令牌校验,解析用户身份 |
| WebSocket 认证 | 建立 WebSocket 连接时需在握手请求中携带有效令牌;令牌验证通过后才建立会话,分配连接到对应用户 |
| 密码安全 | 用户密码使用 BCrypt 算法(强度=10)进行单向哈希,数据库中不存储明文密码和可逆加密密码;密码找回通过验证码重置而非解密 |
输入过滤
| 措施 | 说明 |
|---|---|
| XSS 过滤 | 全局请求体拦截器对所有非文件上传请求的文本内容进行 HTML 实体编码,防止 XSS 注入内容存入数据库后在其他客户端执行 |
| 敏感词过滤 | 消息内容在持久化前经过 DFA 敏感词过滤,命中配置的敏感词时进行替换或拒绝 |
| 文件类型校验 | 上传文件时校验文件扩展名和 Content-Type 是否在允许的白名单内,拒绝可执行文件等危险类型 |
配置层面
| 说明 |
|---|
跨域配置初始设置为允许所有来源(*),适用于开发和测试阶段;后续版本收紧为通过配置注入指定来源 |
| 生产配置与开发配置分离,通过 Spring Profile 隔离;但 v1.0.0 阶段生产配置中仍有部分明文凭据(后续版本全面外化为环境变量) |
| Swagger API 文档在所有环境默认开放;后续版本在生产环境关闭 |
Pingnex © 2026. 基于 box-im 二次开发,保留原始开源协议。