1. 文档定位

本文说明 DGJ2.0 浏览器、PC 客户端、App、PDA、商城 Web 的登录与账号安全链路。重点不是“输入用户名密码”这一小段,而是登录请求经过账号状态、站点端开关、密码兼容、异地验证、设备绑定、客户端版本、授权次数、Session、单设备挤下线和强制重登的完整决策过程。

本文也区分“登录认证”和“业务权限”:登录成功只证明身份和端能力可用,菜单、资源、数据权限仍由第 19 篇权限体系继续判断。

2. 业务边界

2.1 包含

  • Web、PC、App、PDA、商城 Web 五种登录类型。
  • 密码、短信验证码、一键登录、静默登录和内部 token。
  • 登录频率和每日错误次数限制。
  • PC 机器码、版本、客户端形态、授权次数、绑定/解绑申请。
  • App 设备记录和账号挤下线。
  • Session 建立、检查、销毁和强制重登标记。
  • 服务站 PC/App/PDA/商城/快小九/机器人开关。
  • 财务锁定、OPS 配置、缓存失效和安全日志。
  • 忘记密码和短信重置。

2.2 不包含

  • 菜单、按钮、接口资源和数据权限,见第 19 篇。
  • 总分店身份切换和总部账号业务,见第 36 篇。
  • PDA 仓储作业,见第 26 篇。
  • 支付、银行卡、用户实名认证和风控授信。

3. 五种登录类型

LoginType:

类型值入口/Service附加门槛
浏览器 WebDgjwebpassport/Login::signIn -> DgjwebSer账号、密码、端开关
PC 客户端DgjpcpcSignIn -> DgjpcSertoken、机器码、版本、授权、异地验证
AppDgjappApp Login -> DgjappSer设备、短信/一键、单设备登录
PDADgjpdapda/User -> DgjpdaSerPDA 开关、MD5 密码、24h Session
商城 WebKzmallwebKzmallwebSer商城开关、外部 TokenProvider
flowchart LR
    R[登录请求] --> F[UserFactory]
    F --> W[DgjwebSer]
    F --> P[DgjpcSer]
    F --> A[DgjappSer]
    F --> D[DgjpdaSer]
    F --> K[KzmallwebSer]
    W --> S[CI Session]
    P --> S
    A --> S
    D --> S
    K --> T[商城 Token]

4. 参与对象

对象关键标识作用
用户账号uid、username、sid、roleid身份和所属站点
服务站sid登录开关、区域、主账号
PC 机器machine_code/c设备绑定与授权
PC 版本v/version_id客户端包和升级
客户端形态client老单机/新单机/集成/体验/Tauri
App 设备device单设备登录和挤下线
Sessionsession ID登录上下文
安全配置端关闭字段是否允许各端登录

5. 入口地图

5.1 Passport Controller

方法作用主要输入
checkSession检查 Web SessionCookie/Session
signIn浏览器登录用户名、密码、token 等
silentSignIn按 sid 静默登录sid、入口鉴权
pcSignInPC 客户端登录用户名、密码、机器码、版本、client
pcSmsSend绑定/授权短信sid、业务类型
pcActiveBind短信主动绑定sid、机器码、验证码
pcGrantAuth短信增加/授权机器sid、验证码
pcBindApply提交绑定申请机器码、sid
pcUnbind提交解绑申请机器码
signOut销毁 Session当前 Session
getLoginedMobile忘记密码预检查username
sendSmsCode重置密码短信mobile
resetUserPassword重置主账号密码手机号、验证码、新密码

5.2 其他入口

端Controller/Service
Appapplication/controllers/app/passport/Login.php、DgjappSer
PDAapplication/controllers/pda/User.php、Pda/UserSer 或 DgjpdaSer 历史入口
账号安全application/Services/Safe/AccountSafeSer.php
PC 管理application/Services/Pc/*
安全日志AccountSafeLogSer、tasks/AccountSafeLog.php

6. 核心表

表关键键作用
t_sys_adminuid、username、sidDGJ 用户账号
t_sys_admin_configsid + uid员工账号扩展配置
t_app_deviceuid + deviceApp 设备登录记录
t_sys_pc_authssid、授权码PC 可绑定数量和有效期
t_sys_pc_versions版本号PC 版本、下载地址、上线状态
t_sys_pc_machinesmachine_code + sid机器与站点绑定
t_sys_pc_login_controlsidPC 登录控制
t_sys_pc_upgrade_log版本、机器PC 升级审计
t_sys_pc_bind_apply机器码、sid绑定申请
t_sys_pc_unbind_apply机器码解绑申请
t_sys_ops_menu_rule 相关sid各端关闭开关
t_bs_account_safe_logsid、操作人账号安全配置日志

7. Redis 与 Session

Key/机制作用
ci_session:%s用户 CI Session
dgj:rateLimit:login*60 秒登录频率
dgj:login.limit.{username}.{date}每日密码错误计数,达到阈值短时限制
HASH_PC_STATION_STATUSPC 端开关缓存
HASH_APP_STATION_STATUSApp 端开关缓存
HASH_PDA_STATION_STATUSPDA 端开关缓存
HASH_KZMALLWEB_STATION_STATUS商城 Web 开关缓存
HASH_RE_LOGINApp/PDA 用户强制重登标记
REMOTE_LOGIN_WHITE_LIST异地登录豁免站点
app_mobile_login_tmp:*App 手机号选站一次性凭证
USER_RIGHTS登录后业务权限缓存,不是认证状态

8. 通用登录门面

UserFactory(type) 动态实例化 App\Services\User\{type}Ser。

sequenceDiagram
    participant C as Login Controller
    participant F as UserFactory
    participant L as 具体登录Service
    participant Log as login_log
    C->>F: login(type, params)
    F->>F: 获取真实IP/用户名
    F->>F: 60秒最多6次
    F->>L: login(params)
    L-->>F: status/msg/data
    F->>Log: IP、端、结果、sid、uid、machine
    F-->>C: 统一登录结果

9. 两层频控

9.1 UserFactory 频控

同登录名 60 秒允许 6 次(代码注释写 5 次,配置为 [60 => 6])。超过返回“登录频率过快”。

9.2 Passport 密码错误限制

startLogin 使用:

dgj:login.limit.{username}.{YYYYMMDD}

失败时自增并设置 300 秒过期,达到 10 次拒绝。由于每次失败都重新 expire(300),更接近“最后一次失败后 5 分钟”。Key 名含日期但 TTL 只有 5 分钟。

10. Web 登录

flowchart TD
    A[signIn] --> B[beforeLogin清洗参数]
    B --> C{记住密码?}
    C -- 是 --> D[用户名和密码写Cookie]
    C -- 否 --> E[清空Cookie]
    D --> F[UserFactory Dgjweb]
    E --> F
    F --> G{账号/密码/开关通过?}
    G -- 否 --> X[错误计数+失败日志]
    G -- 是 --> H{首次登录?}
    H -- 是 --> I[返回首次登录,不建正式Session]
    H -- 否 --> J[saveSession]
    J --> K[桌面首页]

beforeLogin 会在“记住密码”时将 userpwd 写 Cookie,保存时长 3,600,000 秒。客户端存明文密码属于高风险历史实现,应改为不可逆登录凭证或浏览器密码管理器,且 Cookie 必须 Secure/HttpOnly/SameSite。

11. Web Session 内容

saveSession 写 jxcsys:

字段含义
uid/sid用户和服务站
name/username显示和登录账号
login固定 kzmall 历史值
areaCode/province/eparchy/city区域上下文
needChangePwd是否强制改密
roleid主/子账号角色
headOfficeAccount可选总部账号上下文

Session 是后续业务注入 JXCSID/JXCUID 的来源。禁止前端直接传 SID 覆盖 Session 真值。

12. 首次登录

当前特殊规则:

  • 非生产环境强制 is_first_login = 0。
  • 来源 IP 等于硬编码内网地址时跳过首次登录。
  • 子账号 roleid != 0 跳过首次登录。
  • 主账号首次登录时可能不立即保存 Session,由前端引导安全步骤。

硬编码 IP 和环境分支必须在部署文档中登记,避免迁移后绕过或误拦。

13. 静默登录

silentSignIn(sid) 调 DgjwebSer::silentLogin 后直接保存 Session。它是高权限旁路:

  • 必须由可信 OPS/内部入口调用。
  • 必须记录原操作人、目标 sid 和原因。
  • 不能只依赖可猜的 sid。
  • 切回原身份时应销毁目标 Session。

14. PC 请求字段

{
  "username": "USER_DEMO",
  "userpwd": "******",
  "token": "PAGE_NONCE",
  "c": "MACHINE_CODE_DEMO",
  "v": "VERSION_NUMBER",
  "client": 5
}
字段作用
token页面/请求防伪 token;内部服务走 InnerTokenAuth
c机器码
vPC 软件版本号
client客户端形态枚举 1-5

15. PC 客户端类型

值名称
1老单机版
2新单机版
3集成版
4最新体验版
5Tauri 版

DgjpcSer::checkPcLogin 首先要求 client 存在于该映射。增加新客户端必须同步登录校验、机器表、升级策略和统计页面。

16. PC 登录决策树

flowchart TD
    A[PC登录] --> B{请求token有效?}
    B -- 否 --> X[拒绝]
    B -- 是 --> C{用户名密码机器码版本client齐全?}
    C -- 否 --> X
    C -- 是 --> D[查普通账号或总部账号]
    D --> E{账号正常?}
    E -- 否 --> X
    E -- 是 --> F{站点PC开关正常?}
    F -- 否 --> X
    F -- 是 --> G{内部服务请求?}
    G -- 是 --> H[InnerTokenAuth验证]
    G -- 否 --> I[SMH加密或MD5密码兼容]
    I --> J{需要异地登录验证?}
    J -- 是 --> K[IP/设备/短信验证]
    J -- 否 --> L[机器码与版本校验]
    K --> L
    H --> L
    L --> M{绑定/授权通过?}
    M -- 否 --> N[返回绑定/授权动作数据]
    M -- 是 --> O[构建登录数据和Session]

17. PC 账号与密码

  • 先按用户名/手机号查普通账号。
  • 未找到时查询总部账号。
  • status 必须正常。
  • 有 sid 时检查 PC 站点开关缓存。
  • 内部服务请求用 InnerTokenAuth 校验传入 userpwd。
  • 普通请求兼容 encrypt(...,'SMH') 和 md5(password) 两种历史密码。

密码迁移必须覆盖两种历史格式,不能只改一个端。

18. 异地登录

非首次登录、非内部服务、生产环境、站点不在白名单时可能调用 DgjCommonSer::checkRemoteLogin。LoginOptionEnums::REMOTE_LOGIN_CHECK = false 与实际分支/其他配置的关系需确认。

校验通常依赖 IP、机器和短信。排查异地登录失败时同时查:

  • 真实客户端 IP 是否被代理覆盖。
  • 白名单 Redis。
  • 主/子账号分支。
  • 短信手机号和验证码。
  • 客户端机器码是否变化。

19. PC 机器码自动记录

checkPcLogin 查不到机器码时,会先调用 addMachinesCode(sid, code, version) 记录机器。机器记录存在不等于已授权;还要看状态、SID 绑定和规则开关。

机器表需要区分:

  • 物理机器码。
  • 关联站点。
  • 版本 ID。
  • 客户端形态。
  • 状态/软删。
  • 使用信息和错误说明。

20. PC 登录规则开关

Redis HASH_ZGJ_PC_RULE_LOGIN[sid] 为真时,服务站必须遵守机器绑定/授权规则。白名单旧逻辑已注释。

规则关闭时仍可能记录机器码,但不按严格绑定拦截。排查“同机器某站可登、某站不可登”应先查站点规则。

21. PC 授权次数

t_sys_pc_auths 保存站点授权:

字段语义说明
num可授权总数
use_num已使用数量
over_time过期时间
auth_code授权批次/码

AuthsModel::findAvailableOneBySid 用于新机器判断。授权不足、过期或已用完时返回绑定/申请流程,而不是建立 Session。

22. PC 绑定方式

22.1 主动短信绑定

sequenceDiagram
    participant U as 用户
    participant C as Login Controller
    participant S as BindSer
    participant SMS as 短信
    participant DB as PC Tables
    U->>C: pcSmsSend(sid,bizType=1)
    C->>SMS: 绑定服务站验证码
    U->>C: pcActiveBind(sid,c,sms_code)
    C->>S: activeBind
    S->>SMS: 校验验证码
    S->>DB: 机器与站点绑定/关闭申请

22.2 绑定申请

pcBindApply(c,sid) 写绑定申请,等待后台审核后 BindSer::bind 建立关系。

22.3 授权机器码

pcGrantAuth(sid,smsCode) 用“授权机器码”短信业务处理授权,不应与绑定站点验证码混用。

23. PC 解绑

用户 pcUnbind(c) 先写解绑申请。后台通过 UnbindSer::unbind:

  • 若指定 sid,软删该机器与站点关系。
  • 未指定时可能软删机器全部关系。
  • 关闭同机器解绑申请。
stateDiagram-v2
    [*] --> 未绑定
    未绑定 --> 绑定申请中: pcBindApply
    未绑定 --> 已绑定: 短信主动绑定
    绑定申请中 --> 已绑定: 后台bind
    已绑定 --> 解绑申请中: pcUnbind
    解绑申请中 --> 未绑定: 后台unbind
    已绑定 --> 禁用: MachinesSer reversal
    禁用 --> 已绑定: reversal

24. PC 版本

VersionsSer 支持新增、列表和设为上线;MachinesSer 支持按地区/站点更新版本、查询升级、版本反查站点和升级日志。

关系说明
版本号 -> version_id登录时映射
机器 -> 版本 ID当前/目标版本关系
版本上线状态当前可用包
升级日志操作人、机器集合、版本和参数

未知版本在某些调用 throwEnable=false 时可能记录 version_id=0;严格登录路径是否拒绝需按 afterLogin 继续核对。

25. App 登录方式

App 支持:

  • 账号密码。
  • 手机短信验证码。
  • 运营商一键登录 accessToken。
  • 一个手机号关联多个站点时,先返回可选站点,再用临时 token 选账号。
flowchart TD
    A[App手机登录] --> B{one_click还是sms_code}
    B -- one_click --> C[OneClickLoginProvider换手机号]
    B -- sms_code --> D[SmsProvider校验验证码]
    C --> E[按手机号查在职员工和有效账号]
    D --> E
    E --> F{只有一个有效账号?}
    F -- 是 --> G[直接构建登录数据]
    F -- 否 --> H[生成app_mobile_login_tmp临时凭证]
    H --> I[用户选择站点/账号]
    I --> J[校验临时凭证并立即删除]
    J --> G

26. App 手机号选站

有效账号条件包含:

  • 员工未删除。
  • 手机号匹配。
  • 员工未禁用且在职。
  • 已绑定 adminAccount。
  • SYS_ADMIN 未删除、状态启用。

临时 token 读取手机号后,在成功选站时立即删除,防止重放。需确认 TTL、随机强度和是否绑定设备/IP。

27. App 单设备登录

DgjappSer::add_device(device, uid):

  1. 将该 UID 所有设备记录置 isDelete=1 并写登出时间。
  2. 查当前 uid + device 历史记录。
  3. 不存在则新增。
  4. 存在且已退出则恢复 isDelete=0 并写登录时间。
sequenceDiagram
    participant N as 新设备
    participant S as DgjappSer
    participant DB as APP_DEVICE
    N->>S: 登录(uid,deviceB)
    S->>DB: uid全部设备isDelete=1/logout_time
    S->>DB: 查uid+deviceB
    alt 首次设备
        S->>DB: INSERT deviceB active
    else 历史设备
        S->>DB: UPDATE deviceB active
    end

接口鉴权层必须在每次请求检查当前设备是否仍 active,否则“记录挤下线”不会真正阻止旧 Session 使用。

28. App 短信和一键频控

动作Key限制
手机登录手机或设备 MD560 秒 6 次
发送登录验证码手机号60 秒 1 次

短信 Provider 以 uid 校验验证码;手机号绑定多个账号时使用首个有效 UID 作为校验主体,选站后再绑定具体账号。

29. PDA 登录

DgjpdaSer:

  1. 校验用户名和密码非空。
  2. 查询正常用户。
  3. 检查 PDA 站点开关缓存。
  4. 检查账号状态。
  5. 仅兼容 md5(password)。
  6. 读取站点系统信息构造个人/公司资料。
  7. 写区域 Cookie。
  8. 清除 UID 的 PDA 强制重登标记。

Pda/UserSer 登录成功后将 Session 失效设置为 86400 秒,并返回 sessionId。

flowchart LR
    A[PDA账号密码] --> B[账号状态]
    B --> C[PDA站点开关]
    C --> D[MD5密码]
    D --> E[公司/个人资料]
    E --> F[清PDA强制重登]
    F --> G[24h Session]

30. 站点端开关

AccountSafeSer 从 OPS 菜单规则配置读取:

字段控制端
is_close_pcPC
is_close_appApp
is_close_pdaPDA
is_close_kzamll_web商城 Web
is_close_sass快小九/SAAS
is_close_robot机器人

配置值 1 表示关闭,0 表示开启;登录缓存返回值 1 表示正常、2 表示不正常。两套 1/0/2 语义不能混用。

31. 端开关缓存

sequenceDiagram
    participant L as 登录Service
    participant R as Redis
    participant DB as OpsMenuRule
    L->>R: hGet(key,status)
    alt 缓存命中
        R-->>L: 1正常或2关闭
    else 未命中
        L->>DB: 查询is_close_xxx
        DB-->>L: 0/1
        L->>R: 写1或2
        L-->>L: 返回登录状态
    end

setAccountConfig 更新数据库后调用 unsetStatusCache(sid) 删除 PC/App/PDA 缓存;商城缓存由专用方法删除。新增端开关必须同步缓存失效。

32. 设置账号安全配置

setAccountConfig(sid, accountConfig, apiUser):

  1. 读取新旧 PC/App/PDA/SAAS/机器人开关。
  2. 快小九或机器人一旦关闭,当前代码不允许再次开启。
  3. 关闭 SAAS 时发送 MQ 解绑服务站客户并退出登录。
  4. 关闭机器人时删除该站客户群 ID。
  5. 保存操作人姓名和编码。
  6. 删除端开关缓存。
  7. 若 PC/App/PDA 全关,将站点主账号状态改为删除/禁用。
  8. 任一端开启时,将主账号恢复正常。

33. 关闭不可逆分支

当前代码:

  • is_close_sass 从 1 改 0:抛“快小九账号关闭,无法再次开启”。
  • is_close_robot 从 1 改 0:抛“机器人账号已关闭,无法再次开启”。

这不是普通开关,而是含解绑/清群的不可逆业务操作。页面必须二次确认,并展示影响范围;恢复只能走专门的重新开通流程。

34. 全端关闭与账号状态

flowchart TD
    A[更新PC/App/PDA开关] --> B{三端都关闭?}
    B -- 是 --> C[主账号status=DELETED/0]
    B -- 否 --> D{任一端开启?}
    D -- 是 --> E[roleid=0主账号status=NORMAL/1]
    C --> F[清端缓存]
    E --> F

端开关和账号状态是两层。解开一个端时会恢复主账号,但子账号状态如何恢复需单独确认。

35. 强制重新登录

HASH_RE_LOGIN 按设备类型建 Hash:

  • App 类型 1
  • PDA 类型 2

setNeedReLogin(uid) 同时给 App 和 PDA 写标记;登录成功分别 clearReLogin。业务请求中必须调用 needReLogin 才能真正拦截旧会话。

stateDiagram-v2
    [*] --> 正常会话
    正常会话 --> 必须重登: setNeedReLogin(uid)
    必须重登 --> 登录页: 请求中检测needReLogin
    登录页 --> 正常会话: 新登录clearReLogin

36. 财务锁定

lockPdaAndApp(sid):

  • 关闭 App、PDA、商城 Web。
  • 操作人写“财务”。
  • 清 App/PDA/商城缓存。
  • 调用 TokenProvider::dgjAccountLoginOut 退出该站所有商城账号。

unLockPdaAndApp 恢复三个开关并清缓存。它不直接清每个用户的 CI Session,旧 Session 的失效依赖各端请求校验。

37. 忘记密码流程

sequenceDiagram
    participant U as 用户
    participant C as Login Controller
    participant S as LoginService
    participant R as Redis
    participant SMS as SmsProvider
    U->>C: getLoginedMobile(username)
    C->>S: verifyUser
    S->>S: 仅roleid=0主账号可重置
    C->>R: mobile -> uid, 带TTL
    C-->>U: 脱敏手机号
    U->>C: sendSmsCode(mobile)
    C->>SMS: sendVerifier(uid,mobile)
    U->>C: resetUserPassword(mobile,code,password)
    C->>SMS: match(uid,code)
    C->>S: 更新密码

38. 重置密码边界

  • 子账号 roleid != 0 不允许该流程。
  • 用户名查询后将 mobile -> uid 临时写 Redis。
  • 发送短信又按手机号查用户,需防同手机号多账号歧义。
  • 短信开关关闭时发送接口直接返回成功,重置接口是否仍校验需联调。
  • resetUserPassword 接收的 password 是否已在 Controller 加密需核对后半段代码,禁止存明文。
  • 改密后应给该 UID 设置强制重登并撤销所有旧 Session/设备 token。

39. Session 生命周期

端建立检查销毁/失效
Web/PCsaveSessioncheckSession/login_sessionsignOut sess_destroy
App登录 Service +设备记录API Session/设备 active/重登标记设备挤下线、Session 过期
PDAPda/UserSerPDA API Session24h、重登标记
商城TokenProvider商城 tokendgjAccountLoginOut

CI Session 配置来自环境 session.php。生产 Cookie 的 Secure、HttpOnly、SameSite、域、IP 匹配和 ID 重生成必须以部署配置确认。

40. 登录日志

UserFactory::addLoginLog 写 login_log:

字段内容
ip真实 IP
channel登录类型
params请求 JSON
status结果
sid/uid成功上下文
macPC 机器码

当前直接 json_encode($params) 可能把密码、验证码、accessToken 和临时 token 写日志;内部服务还单独写 auth_token。这是高风险敏感日志问题,应在日志入口统一脱敏或字段白名单。

41. 账号安全日志

t_bs_account_safe_log 和 AccountSafeLogSer 用于记录 OPS/财务配置变更;初始化任务补历史日志。至少应记录:

  • sid
  • 变更前后开关
  • 操作人/来源
  • 时间和原因
  • 是否触发 SAAS 解绑、机器人清群、商城登出
  • 执行结果和失败步骤

42. PC 数据关系

erDiagram
    SYS_ADMIN ||--o{ PC_MACHINE : logs_in
    STATION ||--o{ PC_AUTH : owns
    STATION ||--o{ PC_MACHINE : binds
    PC_VERSION ||--o{ PC_MACHINE : installed
    PC_MACHINE ||--o{ BIND_APPLY : applies
    PC_MACHINE ||--o{ UNBIND_APPLY : applies
    PC_VERSION ||--o{ UPGRADE_LOG : target

43. 登录数据流

flowchart LR
    I[用户名/手机/验证码] --> A[身份校验]
    A --> B[账号状态]
    B --> C[站点端开关]
    C --> D[设备/机器/版本]
    D --> E[异地/短信/一键]
    E --> F[构建sid/uid/role/区域]
    F --> G[Session/Token]
    G --> H[菜单和业务权限]

44. 状态矩阵

状态层正常异常处理
用户账号status=10/其他拒绝登录
站点端开关DB is_close=01拒绝该端
缓存登录状态12拒绝该端
机器绑定active未绑定/禁用绑定、申请或拒绝
授权有剩余且未过期用完/过期申请授权
App 设备isDelete=01旧设备退出
强制重登无 Hash 字段有字段返回登录页
Session有效过期/销毁重新登录

45. 典型失败:密码正确仍不能登录

flowchart TD
    A[密码正确仍失败] --> B{登录频率/错误次数?}
    B -- 超限 --> C[等待TTL/查Redis]
    B -- 正常 --> D{账号status正常?}
    D -- 否 --> E[查账号/全端关闭]
    D -- 是 --> F{该端开关正常?}
    F -- 否 --> G[查AccountSafe配置和缓存]
    F -- 是 --> H{PC?}
    H -- 是 --> I[机器码/版本/client/授权/异地]
    H -- 否 --> J{App/PDA?}
    J -- 是 --> K[设备/重登/短信/Session]

46. 典型失败:PC 提示绑定/授权

排查顺序:

  1. client 是否在 1-5。
  2. v 是否能映射版本。
  3. 机器码是否存在、是否软删、是否禁用。
  4. 机器码是否绑定当前 sid,而不是只绑定其他站。
  5. 站点是否启用严格 PC 登录规则。
  6. AuthsModel::findAvailableOneBySid 是否有剩余授权和有效期。
  7. 是否已有绑定/解绑申请未关闭。
  8. 短信验证码业务类型是否正确。

47. 典型失败:修改开关不生效

检查预期
DB 配置is_close_xxx 已更新
Redis对应端状态 Key 已删除
下次登录回源 DB 并写 1/2
已登录请求每次请求仍校验开关/重登
商城外部 token 已登出
SAAS解绑 MQ 已成功消费
机器人群 ID 已清除

48. 典型失败:App 旧设备仍可用

  1. 查 t_app_device 是否旧设备已 isDelete=1。
  2. 查新设备是否 active。
  3. 查 API 中间件是否每次核对设备。
  4. 查旧 CI Session 是否仍有效。
  5. 查 needReLogin(uid, APP)。
  6. 查客户端是否缓存离线数据而误以为在线。

49. 只读 SQL:账号

SELECT uid, sid, username, mobile, roleid, status, isDelete,
       is_first_login, needChangePwd, create_time, modify_time
FROM t_sys_admin
WHERE username = 'USER_DEMO' OR uid = 10001;

禁止查询或导出密码字段到工单和文档。

50. 只读 SQL:PC 机器与授权

SELECT id, sid, machine_code, status, version_id, client_version,
       is_delete, use_info, error, create_time
FROM t_sys_pc_machines
WHERE machine_code = 'MACHINE_DEMO'
ORDER BY id;
SELECT id, sid, num, use_num, over_time, introduction, create_time
FROM t_sys_pc_auths
WHERE sid = 20001
ORDER BY id DESC;
SELECT id, number, name, status, url, create_time
FROM t_sys_pc_versions
WHERE id IN (
  SELECT version_id FROM t_sys_pc_machines WHERE machine_code = 'MACHINE_DEMO'
);

51. 只读 SQL:申请和设备

SELECT * FROM t_sys_pc_bind_apply
WHERE machine_code = 'MACHINE_DEMO'
ORDER BY id DESC;
SELECT * FROM t_sys_pc_unbind_apply
WHERE machine_code = 'MACHINE_DEMO'
ORDER BY id DESC;
SELECT id, uid, device, isDelete, login_time, logout_time
FROM t_app_device
WHERE uid = 10001
ORDER BY login_time DESC;

52. Redis 只读排查

生产使用项目受控工具,不使用广域 KEYS:

GET dgj:login.limit.USER_DEMO.20260716
HGET <PC_STATUS_KEY_FOR_SID> status
HGET <APP_STATUS_KEY_FOR_SID> status
HGET <PDA_STATUS_KEY_FOR_SID> status
HGET <RE_LOGIN_APP_HASH> 10001
HGET <RE_LOGIN_PDA_HASH> 10001

真实前缀和格式由 RedisKeys 与环境前缀共同决定。

53. 代码定位

rg -n "function (signIn|pcSignIn|silentSignIn|signOut|resetUserPassword)" \
  application/controllers/passport/Login.php

rg -n "class UserFactory|RateLimit|addLoginLog|mobileLogin" application/Services/UserFactory.php

rg -n "checkPcLogin|addMachinesCode|afterLogin|REMOTE_LOGIN" \
  application/Services/User/DgjpcSer.php

rg -n "add_device|mobileLoginSelectStation|sendMobileLoginSmsCode" \
  application/Services/User/DgjappSer.php

rg -n "LoginStatusCache|setAccountConfig|needReLogin|lockPdaAndApp" \
  application/Services/Safe/AccountSafeSer.php

rg -n "class (BindSer|UnbindSer|AuthsSer|MachinesSer|VersionsSer)" application/Services/Pc

54. 安全问题清单

风险当前证据建议
明文密码 CookiebeforeLogin 写 userpwd移除,改短期设备凭证
MD5 历史密码PC/PDA/轮胎等渐进迁移 Argon2/bcrypt
登录日志保存请求params=json_encode白名单+脱敏
内部 auth token 日志auth_token 字段禁止持久化明文
Session 固定需查生产配置登录成功重生成 ID
静默登录按 SID强认证+审计+短时权限
设备挤下线只更新设备表中间件每请求校验
多状态语义0/1/2 混用强类型枚举和值对象

55. 补偿 SOP:机器绑定错站

  1. 导出机器全部 SID 关系、状态、版本和申请。
  2. 确认物理机器所有人和目标站点。
  3. 关闭未处理的相反申请。
  4. 使用 UnbindSer 软删错误关系,不物理删历史。
  5. 使用正常绑定流程关联目标站点。
  6. 验证授权次数、机器登录和旧站不可登录。

56. 补偿 SOP:端开关缓存错

  1. 查 DB 真值和修改日志。
  2. 删除单个 SID 对应端缓存,不做全库清理。
  3. 重新调用只读状态接口让缓存回源。
  4. 验证新登录和已有会话。
  5. 若数据库被错误修改,走审计审批恢复,而不是只改 Redis。

57. 补偿 SOP:改密后旧会话仍有效

  1. 确认密码已按正确格式落库。
  2. setNeedReLogin(uid) 标记 App/PDA。
  3. 销毁/撤销 Web/PC Session。
  4. 将 App 设备全部置退出,要求新设备重新登录。
  5. 调用商城 Token 登出。
  6. 验证旧 Cookie、Session ID、设备和商城 token 全失效。

58. 禁止动作

  • 在日志、群聊、文档打印密码、验证码、accessToken、内部 token。
  • 直接把账号状态改 1 绕过端关闭原因。
  • 只删除 Redis 缓存当成永久解锁。
  • 物理删除机器、绑定和安全日志。
  • 未确认设备归属就跨站绑定机器码。
  • 为恢复机器人/SAAS 直接把不可逆开关改回 0。
  • 修改 Session 数据伪造 sid/uid/roleid。

59. 监控建议

指标维度告警
登录成功率端、版本、地区显著下降
失败原因密码/状态/开关/机器/验证码异常集中
登录频控触发用户/IP/设备暴力尝试
异地验证量地区、站点突增
机器绑定/解绑失败机器、站点连续失败
授权用尽站点SID提前预警
App 设备挤下线UID异常频繁
强制重登未执行UID/端超 SLA
安全开关变更操作人/端全量审计
敏感字段日志命中日志源任一命中

60. 回归清单:Web 与密码

  • [ ] 正确/错误用户名密码。
  • [ ] 用户名和手机号登录。
  • [ ] SMH/MD5 历史密码。
  • [ ] 账号禁用、临时关闭、全端关闭。
  • [ ] 60 秒频控和错误 10 次限制。
  • [ ] 首次登录、子账号、非生产和内网特殊分支。
  • [ ] 记住密码 Cookie 移除/兼容。
  • [ ] Session ID 重生成、检查和退出。
  • [ ] 静默登录权限和审计。
  • [ ] 忘记密码主账号、子账号、短信失败、验证码错误。
  • [ ] 改密后所有旧会话失效。

61. 回归清单:PC

  • [ ] 缺少 token/机器码/版本/client。
  • [ ] client 1-5 和非法值。
  • [ ] 普通账号与总部账号。
  • [ ] PC 端开关和缓存。
  • [ ] 严格规则开启/关闭。
  • [ ] 新机器首次记录。
  • [ ] 机器绑定当前站、其他站、禁用、软删。
  • [ ] 授权足够、用尽、过期。
  • [ ] 主动短信绑定和后台绑定申请。
  • [ ] 授权短信业务类型。
  • [ ] 解绑申请、单站解绑和全机器解绑。
  • [ ] 异地登录白名单、短信和 IP。
  • [ ] 版本不存在、升级和回退。
  • [ ] 并发登录同一机器/同一站点。

62. 回归清单:App、PDA 与安全开关

  • [ ] App 账号密码、短信、一键登录。
  • [ ] 手机号无账号、单账号、多站点选站。
  • [ ] 临时 token 过期、重复和跨设备。
  • [ ] 发送短信 60 秒频控。
  • [ ] App 新设备登录挤下旧设备。
  • [ ] 旧设备 API 请求被拒绝。
  • [ ] PDA 正确/错误 MD5 密码。
  • [ ] PDA 24 小时 Session。
  • [ ] App/PDA 强制重登标记和清除。
  • [ ] PC/App/PDA 单独关闭和组合关闭。
  • [ ] 三端全关禁用主账号,重新开一端恢复。
  • [ ] 财务锁定/解锁和商城登出。
  • [ ] SAAS 关闭解绑 MQ,不可逆重开。
  • [ ] 机器人关闭清群,不可逆重开。
  • [ ] 配置变更缓存失效和安全日志。

63. 改动风险

级别代码/行为最低回归
P0UserFactory、Session、密码验证全端登录和旧密码迁移
P0AccountSafeSer 开关/全端关闭全端、账号状态、SAAS/机器人副作用
P0设备挤下线/强制重登中间件App/PDA 所有受保护 API
P1DgjpcSer 机器/版本/授权全 PC 客户端和绑定流程
P1忘记密码/短信/一键登录频控、重放、多账号、安全
P1登录日志可观测性和敏感字段
P2登录页、背景图、提示文案各端兼容和错误码

64. 已确认事实

  • UserFactory 按类型路由登录,并统一限流和写登录日志。
  • Passport 有额外每日用户名错误计数限制。
  • Web Session 保存 UID、SID、角色、区域等上下文。
  • PC 必填 token、机器码、版本和客户端类型。
  • PC 密码兼容 SMH 和 MD5,内部请求用 InnerTokenAuth。
  • PC 客户端类型有 1-5 五种。
  • PC 严格规则开启时检查机器绑定和授权。
  • App 支持短信/一键登录和多站点选择。
  • App 登录会将同 UID 其他设备置退出。
  • PDA 检查独立端开关并清强制重登标记。
  • 端关闭配置用 0/1,缓存登录状态用 1/2。
  • 强制重登分别按 App/PDA Hash 保存。
  • 财务锁定会关闭 App/PDA/商城并登出商城 token。
  • SAAS/机器人关闭带不可逆副作用。

65. 待确认与缺口

  • Web/PC/App/PDA 实际路由和中间件全集。
  • 生产 Session Cookie 安全配置和存储驱动。
  • REMOTE_LOGIN_CHECK=false 与实际异地校验分支关系。
  • PC afterLogin 机器授权所有返回码和状态。
  • PC 表唯一索引、授权并发扣减和机器码稳定性。
  • App 设备 active 是否在每个请求强校验。
  • 强制重登是否覆盖所有 App/PDA API。
  • 手机号多账号短信验证 UID 选择规则。
  • 重置密码最终存储格式和旧会话撤销。
  • 安全日志真实字段、保留期和告警。
  • 登录日志是否已在采集层脱敏。
  • SAAS 解绑 MQ 重试、机器人群 ID 恢复流程。

66. 证据来源

  • application/controllers/passport/Login.php
  • application/controllers/app/passport/Login.php
  • application/controllers/pda/User.php
  • application/Services/UserFactory.php
  • application/Services/User/DgjwebSer.php
  • application/Services/User/DgjpcSer.php
  • application/Services/User/DgjappSer.php
  • application/Services/User/DgjpdaSer.php
  • application/Services/User/KzmallwebSer.php
  • application/Services/Passport/LoginService.php
  • application/Services/Safe/AccountSafeSer.php
  • application/Services/Safe/AccountSafeLogSer.php
  • application/Services/Pc/*
  • application/models/pc/*
  • application/KzData/Enums/LoginType.php
  • application/KzData/Enums/LoginOptionEnums.php
  • application/KzData/Enums/StationEnums.php
  • application/KzData/Enums/RedisKeys.php
  • application/config/tables.php
  • application/config/session.php(环境文件)

67. 一页式交接结论

  1. 登录成功需要依次通过身份、账号状态、端开关、设备/机器、版本/授权和 Session。
  2. Web、PC、App、PDA、商城不是同一套附加规则,排障先确定登录类型。
  3. PC 的机器存在不等于已绑定,绑定不等于有授权,版本正确也不等于端开关正常。
  4. App 单设备登录只有在 API 每次检查设备 active 时才真正有效。
  5. 强制重登是 UID+端 Redis 标记,与站点端关闭缓存不是一回事。
  6. 账号安全 DB 用 0/1,登录缓存用 1/2,不能直接比较。
  7. 三端全关会禁用主账号,重新开任一端会恢复主账号。
  8. SAAS 和机器人关闭包含解绑/清群,是不可逆业务动作。
  9. 明文密码 Cookie、MD5、请求日志敏感字段和静默登录是当前高风险点。
  10. 改密、锁站和设备切换都必须验证旧 Session、旧设备、商城 token 真正失效。

请求-日志-数据变更追踪卡

多入口请求链路

场景调用方与入口请求载荷/上下文Controller/ConsumerService/Provider汇合点最终业务事实
Web/PC 登录/passport/Login、PC 登录账号、密码/验证码、设备、IPPassport ControllerUserFactory、DgjwebSer/DgjpcSeruser/admin ID + session建立登录会话和站点权限
App/PDA 登录/app/passport/Login、pda/User手机/账号、设备 ID、版本、验证码App/PDA ControllerUser/Safe Serviceuser + devicetoken/session 与设备绑定
改密/重登安全中心/管理员操作用户、旧新凭证、强制重登标记Account ControllerAccountSafeSeruser ID密码更新,旧凭证失效
锁站/PC 授权OPS/PC 管理sid、终端类型、设备/白名单管理入口PC/Safe Servicestation+terminal/deviceRedis 开关/授权决定是否允许登录

日志证据矩阵

| 链路段 | 日志来源 | 可检索锚点 | 成功信号 | 失败信号 | 与下一段关联方式 | | --- | --- | --- | --- | --- | --- | --- | | 登录请求 | Login Controller | request_id、账号 hash/用户 ID、sid、设备 ID、IP | 认证成功并生成 session/token | 密码/验证码错、锁定、站点禁用 | 用户 ID 进入权限/设备校验 | | 设备/站点校验 | Safe/PC Service | user、device、terminal、Redis key | 命中授权或白名单 | 设备冲突、终端状态关闭 | station+terminal key 回查 Redis | | 会话建立 | User Service/session/token | session/token ID(日志仅 hash)、user ID | 新会话可访问受权接口 | 会话未保存、上下文 sid 错 | 会话 ID 关联后续 request ID | | 失效操作 | AccountSafeSer | user ID、action、HASH_RE_LOGIN | 旧 session/token/设备请求均拒绝 | 只改密码未清旧会话 | 用户/设备逐端验证 |

环节数据变更台账

步骤代码位置事务读取事实写入表/缓存/MQ字段或数量变化回查证据
查账号Login/UserFactory只读用户状态、站点、密码 hash、权限无零写入;日志不得出现明文密码/tokenuser/admin ID、状态
校验终端PC/Safe Service只读 RedisHASH_*_STATION_STATUS、设备/白名单无allow/deny 计算sid+terminal+device
建会话Dgjweb/Dgjpc/App/PDA Service登录事务/缓存写用户权限和站点session/token、设备关系、登录时间新 session/token insert;last_login old -> nowtoken hash、设备、登录时间
改密/强退AccountSafeSer用户事务 + 缓存清理当前凭证和活跃会话用户表、HASH_RE_LOGIN、session/token 缓存password hash 更新;版本/重登标记 +1;旧会话删除/拒绝各终端旧凭证回归
权限加载BaseController/USER_RIGHTS只读/缓存user role、SYS_ADMIN、资源关系session rights/cacheDB 不变;请求上下文获得权限最小/管理员账号对照

子模块追踪:login-web Web 登录与 Session

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
登录浏览器提交账号凭证request ID、account hash、client/session IDapplication/controllers/passport/Login.php -> application/Services/Passport/LoginService.php密码 hash、账号/站点状态、登录类型和终端开关登录本地事务/缓存写 session none -> active、last_login old -> nowrequest ID + admin/user ID + client type失败零业务写入;日志不含明文密码/token
会话校验后续 Web 请求session hash、user/sidapplication/Services/User/DgjwebSer.phpsession、重登版本、账号和权限状态校验查询只读;过期/失效删除或拒绝 sessionrequest ID + session hash + user/sidSession 有但账号已锁仍拒绝;重新登录而非手改缓存

子模块追踪:login-first-silent 首次登录与静默登录

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
首次登录新账号首次进入user ID、first-login flag、clientapplication/Services/UserFactory.php账号初始化、密码/协议/选站要求用户本地事务 first_login true -> false 或保持待完成;session 按规则建立request ID + user + option/status必做步骤未完成不得绕过;失败不清除首次标记
静默登录有效旧凭证自动续会话token/session hash、device、relogin versionapplication/KzData/Enums/LoginOptionEnums.phptoken、设备、账号和强退标记缓存事务外续期 old expiry -> new,业务 DB 不变request ID + token hash + device强退/改密后静默登录必须失败;旧 token 不恢复

子模块追踪:login-pc PC 登录与客户端类型

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
PC 登录PC 客户端账号登录account、client type/version、machine codeapplication/Services/User/DgjpcSer.php -> application/KzData/Enums/LoginType.php账号、PC 开关、机器授权和客户端类型登录本地事务/缓存建 PC session,登录时间更新request ID + user/sid + client/machineWeb/PC 类型不可混用;未授权设备零业务写入
类型回查登录成功但菜单/行为错误session、login type、user rightsapplication/Services/UserFactory.php实际 Service、客户端类型和权限缓存查询只读 不写request ID + service/type + rights count修正路由/类型后重登,不直接篡改 session 字段

子模块追踪:pc-bind PC 机器码绑定、授权与解绑

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
绑定授权管理员/用户绑定 PC 机器码user/sid、machine hash、actionapplication/Services/User/DgjpcSer.php原绑定、设备上限、白名单和操作权限设备本地事务 unbound -> bound/authorized,写操作时间request ID + user/sid + machine hash同设备/超上限按规则拒绝;机器码只记录 hash
解绑强退解绑或撤销授权relation ID、machine hashapplication/Services/Safe/AccountSafeSer.php绑定关系和该设备活跃 session本地事务 bound -> unbound;commit 后清 PC sessionrequest ID + relation + cleared sessions清缓存失败只补强退,不恢复绑定关系

子模块追踪:login-app App 登录、选站与单设备

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
登录选站App 登录/选择服务站user、device ID、target sid、tokenapplication/controllers/app/passport/Login.php -> application/Services/User/DgjappSer.php用户可用站点、App 开关、单设备规则和旧 token登录本地事务/缓存 token old -> new、currentSid 更新,旧设备失效request ID + user/device + sid + token hashtarget sid 无权零写入;不记录真实 token
单设备验收第二台设备登录或旧设备访问user、two device IDs、relogin versionapplication/Services/Safe/AccountSafeLogSer.php活跃 token、设备关系和安全日志新登录使旧 token active -> invaliduser + device hashes + login times旧设备必须拒绝;缓存失败补清旧 token

子模块追踪:login-pda PDA 登录

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
PDA 登录仓内设备登录user、device、sid、warehouseapplication/controllers/pda/User.php -> application/Services/User/DgjpdaSer.phpPDA 开关、用户仓库权限、账号/设备状态登录本地事务/缓存建 PDA session none -> activerequest ID + user/sid + device/warehousePC/App token 不可复用;无仓库权限零写入
作业校验PDA 后续扫码请求session hash、billNo、locationapplication/Services/User/DgjpdaSer.phpsession、当前站点/仓库和重登标记鉴权查询只读 不写request ID + session hash + billNosession 失效时业务动作不得部分执行

子模块追踪:terminal-switch 站点端开关与缓存

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
配置开关OPS 开停 Web/PC/App/PDAsid、terminal type、status、operatorapplication/KzData/Enums/StationEnums.php原端状态、白名单和站点状态配置本地事务/缓存 terminal old -> enabled/disabledrequest ID + sid + terminal + status禁用后新登录必须拒绝;已登录是否强退按规则处理
缓存回查DB 已改但登录结果旧sid、HASH_*_STATION_STATUS keyapplication/KzData/Enums/RedisKeys.phpDB 配置、Redis 值和更新时间事务外 cache old -> latest/deleted,DB 不变sid + key + hit/value version只清目标站点终端键;Redis 环境事实待验证

子模块追踪:account-security 账号安全配置与强制重登

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
安全配置修改安全策略/账号状态user/sid、policy/action、operatorapplication/Services/Safe/AccountSafeSer.php原策略、账号、活跃会话和操作权限用户本地事务 security/status old -> new、relogin version +1request ID + user/sid + action/version无权操作零写入;安全日志不存凭证明文
全端强退锁定、改密等触发user、session/token keysapplication/Services/Safe/AccountSafeLogSer.phpWeb/PC/App/PDA 活跃凭证commit 后事务外删除/拒绝所有端凭证user + cleared key counts + client types部分清理失败按用户重试;不回滚已生效安全状态

子模块追踪:password-reset 忘记密码与重置

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
验证重置忘记密码验证码/管理员重置user/mobile hash、verify request、operatorapplication/Services/Passport/LoginService.php账号、验证码/权限、密码策略和重放标记用户本地事务 password hash old -> new、relogin version +1request ID + user + reset type + result验证失败零写入;不记录验证码和新密码
旧会话失效重置成功后user、all client sessionsapplication/Services/Safe/AccountSafeSer.php活跃凭证和最新重登版本commit 后清缓存,旧 session/token active -> invaliduser + client/session counts清理失败继续拒绝旧版本并补删缓存

子模块追踪:finance-lock 财务锁站与全端失效

环节入口/触发请求/业务键代码链路读取事实写入与字段变化日志证据异常与补偿
锁站逾期/财务规则触发sid、repayment/lock ID、reasonapplication/Services/Safe/AccountSafeSer.php所有有效锁因、站点状态和现有会话站点本地事务 unlocked -> locked;commit 后全端凭证失效task/request ID + sid + lock reasons + cleared counts多锁因分别保留;锁站失败不应允许新资金动作
解锁全部锁因解除sid、remaining lock IDsapplication/KzData/Enums/AccountEnums.php逾期/黑名单最终事实和当前锁态条件满足才本地事务 locked -> unlocked,用户需重新登录sid + resolved/remaining reasons不恢复旧 token;缓存菜单/终端状态另行刷新