PAYATHON 2026

无法登录 WSO2 API billing engine

数据小陈

结论

appuser 表中有记录,只能证明注册时成功写入了数据库,不能说明登录流程没有问题。排查时先确认以下三点:

  1. 登录服务和注册服务是否连接到同一个 billing 数据库及 schema。
  2. 登录时提交的身份字段是否正确。例如,系统要求 username,实际提交的却是邮箱。
  3. 数据库中的密码格式是否与登录端配置的 PasswordEncoder 匹配。

不要直接修改数据库中的密码字段,也不要将明文密码写入表中。登录时同步查看 billing engine 服务端日志,再结合实际部署包中的认证配置,判断失败发生在用户查询、密码校验还是账户状态检查阶段。

先确认登录请求是否到达 billing engine

打开浏览器开发者工具的 Network 面板,重新提交登录请求,并记录以下信息:

  • 请求 URL 和 HTTP 方法
  • 请求体中的字段名
  • HTTP 状态码
  • 响应内容
  • 是否发生重定向
  • 是否建立了会话 Cookie

也可以从浏览器复制请求为 cURL 后执行,例如:

curl -i -X POST 'https://billing.example.com/<login-path>' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'username=testuser' \
  --data-urlencode 'password=********'

<login-path>、字段名和认证方式都要以实际部署页面发出的请求为准,不能直接照搬示例。

不同状态码通常对应以下排查方向:

  • 404:前端配置的登录路径、反向代理路径或应用 context path 有误。
  • 401:用户不存在、密码校验失败,或认证提供方拒绝登录。
  • 403:账户未启用、权限不足、CSRF 校验失败,或会话策略限制了访问。
  • 500:数据库查询、密码编码、配置文件或后端程序发生异常。
  • 302 后再次返回登录页:可能是会话 Cookie、HTTPS、代理头或登录成功后的跳转地址有问题。

如果服务端日志中完全找不到对应请求,应先检查反向代理、端口、防火墙和前端配置的 billing engine 地址,不要继续修改数据库。

检查注册和登录是否使用同一数据源

这是常见原因之一。注册服务可能将用户写入了一个数据库,而登录模块连接的是另一个数据库、默认数据库或不同的 schema。

核对注册模块和登录模块实际加载的配置,主要比较:

  • JDBC URL
  • 数据库名称
  • schema
  • 数据库用户名
  • JNDI data source 名称
  • 环境变量和启动参数
  • 测试、开发、生产环境配置
  • 容器内配置与宿主机配置

先查看表结构,避免在字段名不确定的情况下直接查询:

DESCRIBE appuser;

然后使用表中实际存在的用户名或邮箱字段查找注册记录:

SELECT *
FROM appuser
WHERE <identity_column> = '<registered_value>';

如果数据库区分大小写,还要核对登录输入与保存值的大小写。实际执行查询时应使用参数绑定,上面的 SQL 只适合人工排查。

修改数据源配置后,需要完整重启 billing engine,并通过启动日志确认它加载了预期配置。只重启前端页面或 API Manager,通常不会重新加载 billing engine 的数据库连接。

确认登录标识和表单字段

注册页面允许填写邮箱、用户名等多个字段,不代表登录时可以随意使用其中任何一个。需要确认登录端实际根据哪个字段查询用户:

  • username
  • email
  • userName
  • 其他自定义字段

表单字段名也要符合后端要求。例如,后端读取 username,前端却提交 email,即使两个字段的值相同,也可能认证失败。

同时检查以下情况:

  • 用户名前后是否带有空格。
  • 邮箱或用户名在注册时是否被转为小写,而登录时没有采用同样的处理规则。
  • 不同租户中是否存在相同用户名。
  • 登录时是否要求携带租户域,例如 user@tenant.example。
  • 当前登录页面是否面向管理员账户,而注册流程创建的是普通应用用户。

是否需要租户域或特定用户名格式,取决于 billing engine 的实现和身份源配置。不能因为 appuser 表中有记录,就认定登录标识正确。

核对密码保存和校验方式

查看 appuser 表中的密码字段时,只判断数据形态,不要复制或公开真实内容:

SELECT <identity_column>, <password_column>
FROM appuser
WHERE <identity_column> = '<registered_value>';

需要核对:

  • 注册端保存的是否为密码摘要,而不是空值或被截断的数据。
  • 数据库列长度能否容纳完整摘要。
  • 注册端和登录端是否使用相同的编码算法。
  • 两端对 salt、迭代次数和算法前缀的处理是否一致。
  • 系统升级或重新部署后,PasswordEncoder 配置是否发生变化。
  • 密码是否在前端或代理层被错误编码、截断或重复转义。

例如,Spring Security 的委派式密码格式可能包含算法标识:

{bcrypt}$2a$10$...

有些实现则只保存:

$2a$10$...

是否需要 {bcrypt} 前缀,要看实际使用的 PasswordEncoder。不要靠手工添加或删除前缀来试错,否则可能损坏已有账户。

如果注册端使用 BCrypt,而登录端错误地采用明文比较,配置思路应类似:

PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

boolean matched = passwordEncoder.matches(
    submittedPassword,
    storedPasswordHash
);

不要这样比较摘要:

submittedPassword.equals(storedPasswordHash);

也不要通过 SQL 将密码更新为明文。更安全的验证方法是修正编码配置后,通过“重置密码”功能重新设置密码,或创建一个新的测试账户。

检查账户状态和权限字段

有些 billing engine 实现除了校验用户名和密码,还会检查账户状态。根据实际表结构确认是否有以下字段或含义相近的字段:

  • enabled
  • active
  • status
  • locked
  • verified
  • role
  • tenant

即使注册成功,账户也可能仍处于未激活、未验证或未分配角色的状态。服务端日志中通常会出现类似 “disabled”、“locked”、“not verified” 或 “authority not found” 的信息。

如果系统要求通过邮件激活,还要检查:

  • 激活链接是否成功生成。
  • 邮件发送配置是否正确。
  • 激活链接中的主机名、端口和协议是否可访问。
  • 激活操作是否更新了正确的数据库。
  • 反向代理是否改写了激活回调地址。

只有弄清字段含义和合法状态值后,才能修改账户状态。不要仅凭字段名称就执行类似 UPDATE appuser SET enabled = 1 的操作,因为不同实现使用的状态值和关联表可能并不相同。

从日志定位具体失败阶段

提交登录请求时,同时查看 billing engine 日志、反向代理日志和数据库异常。可以重点搜索:

Authentication
Bad credentials
UsernameNotFound
PasswordEncoder
DataSource
SQLException
Access denied
CSRF
session
cookie
redirect
disabled
locked

如果现有日志级别不足,可以临时提高认证组件和数据库访问组件的日志级别。但不要在生产环境中长期启用会记录请求体或 SQL 参数的详细日志,以免泄露密码、Cookie 或令牌。

不同 billing engine 构建版本使用的日志框架和 logger 名称可能不同,应以部署包中的 log4j2、log4j、Logback 或 WSO2 日志配置为准。

可以按以下情况判断:

  • 查不到用户:检查数据源、schema、身份字段和大小写。
  • 能查到用户但提示 Bad credentials:检查密码摘要、编码器和数据库列长度。
  • 密码通过但提示 disabled 或 locked:检查激活状态和账户策略。
  • 认证成功后仍返回登录页:检查 Session、Cookie、代理和重定向。
  • 出现 SQL 异常:检查表结构是否与当前 billing engine 构建版本匹配。

检查 HTTPS、Cookie 和反向代理

如果日志显示认证已经成功,但页面仍停留在登录页,问题通常出在会话保持,而不是 appuser 表。

确认浏览器是否收到并回传了会话 Cookie,同时检查:

  • Cookie 的 Domain 和 Path
  • Secure 属性是否与 HTTPS 配置一致
  • SameSite 策略
  • billing engine 的外部访问地址
  • 代理传递的 Host 和 X-Forwarded-* 头
  • 登录成功后的跳转地址
  • 登录过程中是否在 HTTP 与 HTTPS、域名与 IP 或不同端口之间切换

反向代理通常需要保留原始主机和协议。例如,Nginx 可以采用类似配置:

proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

这只是通用示例。具体配置还取决于 billing engine 是否支持并信任这些转发头。

建议的最短排查顺序

  1. 创建一个新的测试账户,确认 appuser 中的记录完整。
  2. 提交一次登录请求,在浏览器中记录请求、响应和 Cookie。
  3. 同时查看 billing engine 日志,找到与本次登录对应的异常。
  4. 确认登录模块查询的是同一个数据库和 schema。
  5. 确认提交的是后端要求的用户名字段。
  6. 检查密码摘要是否完整,以及注册端和登录端使用的编码器是否一致。
  7. 检查账户是否已经启用、验证并获得必要权限。
  8. 如果后端显示认证成功,再检查代理、HTTPS、Session Cookie 和回调地址。
  9. 修正配置并重启服务,然后使用新账户或正规的密码重置流程复测。

如需进一步确定根因,至少应提供脱敏后的登录 HTTP 状态码、响应内容、billing engine 在同一时间点的异常堆栈、appuser 表结构,以及注册端和登录端实际使用的数据源配置。不要提供真实密码、密码摘要、Cookie、数据库凭据或访问令牌。

备注:内容仅供参考。