\App\Services\ApiAuthService

认证业务服务

职责:登录认证域全部业务逻辑——登录/刷新/下线、注册(验证码三步)、找回密码(两步)、 当前用户信息装配;JWT 签入 Redis、会话绝对生命周期、密码盐、验证码校验均在此收敛。 不处理 HTTP 关注点(Cookie 构造等),只返回 Service 数据。

┌────────────────── 分区导航(按此顺序阅读)──────────────────┐ 分区 1 · 构造与依赖注入 构造方法(注入权限缓存 / 配置服务) 分区 2 · 会话流程 login / refresh / logout 分区 3 · 注册流程 sendRegisterCode / verifyRegisterCode / completeRegistration 分区 4 · 找回密码流程 sendResetLink / verifyResetToken / resetPassword 分区 5 · 当前用户信息 userinfo 分区 6 · 内部辅助方法 配置读取 / 频控 / 验证码 / 注册表键解析 └────────────────────────────────────────────────────────────┘

Summary

Methods
Properties
Constants
__construct()
login()
refresh()
logout()
sendRegisterCode()
verifyRegisterCode()
completeRegistration()
sendResetLink()
verifyResetToken()
resetPassword()
userinfo()
changePassword()
updateMyProfile()
mySessions()
revokeSession()
revokeOtherSessions()
getMyPreferences()
updateMyPreferences()
No public properties found
No constants found
guard()
ttlMinutes()
No protected properties found
N/A
notifyNewDeviceLogin()
revokeOneSession()
normalizeMyAvatar()
registerCodeTtl()
registerSendInterval()
resetSendInterval()
registerCodeMaxAttempts()
mailSendMax()
mailSendLockSeconds()
assertMailSendAllowed()
resetTokenTtl()
sessionAbsoluteMaxSeconds()
refreshRotationGraceSeconds()
registerEnabled()
resetEnabled()
registerCodeKey()
resolveRegisterCode()
resolveResetToken()
consumeResetToken()
verifyCaptcha()
$cacheService
$configService
N/A

Properties

$configService

$configService : \App\Services\Admin\ConfigService

Type

ConfigService

Methods

__construct()

__construct(\App\Services\Admin\PermissionCacheService  $cacheService, \App\Services\Admin\ConfigService  $configService) : mixed

Parameters

\App\Services\Admin\PermissionCacheService $cacheService
\App\Services\Admin\ConfigService $configService

Returns

mixed —

login()

login(array  $credentials, string  $captchaKey, array  $captcha, bool  $remember, int  $rememberDays) : array

登录,返回 [md5Key, expiresAt, ttlMin](Cookie 由控制器按 ttlMin 构造)。

流程:① 校验登录锁定状态(超限拒绝,附剩余秒数) ② 校验点选验证码(验证码错误不计入登录锁定,防已知用户名被 DoS 锁定) ③ 手动校验用户名/密码/状态(用户名不存在抹平时序差,防枚举) ④ 签发 JWT 写入 Redis 会话,并记录会话绝对生命周期上限 ⑤ 清除失败计数、失效旧会话、更新库内 token 信息 ⑥ 预加载用户权限缓存并返回

Parameters

array $credentials
string $captchaKey
array $captcha
bool $remember
int $rememberDays

Throws

\InvalidArgumentException

Returns

array —

refresh()

refresh() : array

刷新 token,返回 [md5Key, expiresAt, ttlMin](Cookie 由控制器构造)。

流程:① 取当前会话 Redis token,缺失即拒绝 ② 校验会话绝对生命周期上限,超限强制重新登录 ③ 刷新 JWT(沿用登录时写入的 remember_days 计算 TTL) ④ 轮换会话 key:旧 key 立即失效并写宽限期重定向表,并发标签页在宽限期内追认新 key ⑤ 更新库内 token_key 并刷新权限缓存

Throws

\InvalidArgumentException

Returns

array —

logout()

logout() : void

登出。

流程:① 统一吊销副作用(SessionHelper::revoke:JWT 拉黑 + 删 session/age 键) ② 清库内 token_key 锚点 ③ 清理用户权限缓存

sendRegisterCode()

sendRegisterCode(string  $username, string  $email, string  $captchaKey, array  $captcha) : void

发起注册 —— 第一步:发送邮件验证码(6 位数字)。

流程:① 校验注册开关与点选验证码 ② 60 秒发送频率限制 ③ 用户名/邮箱查重(失败静默忽略,防枚举) ④ 邮件发送频控(次数上限 + 锁定窗口) ⑤ 生成 6 位验证码存 Redis(10 分钟,绑定邮箱)并发送邮件(模板 register_code)

Parameters

string $username
string $email
string $captchaKey
array $captcha

verifyRegisterCode()

verifyRegisterCode(string  $username, string  $email, string  $code) : void

校验注册邮件验证码 —— 第二步:输入验证码通过后进入设置密码。

流程:① 取验证码记录(不存在/用户名不匹配视为失效) ② 比对验证码,错误累计尝试次数(超限作废需重新获取) ③ 通过后标记 verified,完成注册时须已验证

Parameters

string $username
string $email
string $code

completeRegistration()

completeRegistration(string  $username, string  $email, string  $code, string  $password) : void

完成注册 —— 第三步:邮箱验证通过后设置密码并创建用户。

流程:① 校验验证码记录已 verified 且再次比对验证码 ② 生成随机盐,盐+密码哈希后 forceCreate 落库(salt/is_super 不在 fillable) ③ 清理验证码记录与发送频控键

Parameters

string $username
string $email
string $code
string $password

sendResetLink()

sendResetLink(string  $email, string  $captchaKey, array  $captcha) : void

忘记密码 —— 第一步:发送密码重置链接邮件。

流程:① 校验找回开关与点选验证码 ② 60 秒发送频率限制(未注册邮箱同样设置,防枚举差分) ③ 按邮箱查用户(查不到返回统一提示,防邮箱枚举) ④ 邮件发送频控(仅在确要发邮件时计数) ⑤ 生成一次性重置 token(30 分钟,单次使用)并发送邮件(模板 reset_password)

Parameters

string $email
string $captchaKey
array $captcha

verifyResetToken()

verifyResetToken(string  $token) : string

校验重置链接 token,返回用户名(供跳转页展示)。

流程:① 按链接 token 反查缓存条目 ② 查用户 ③ 返回用户名

Parameters

string $token

Returns

string —

resetPassword()

resetPassword(string  $token, string  $newPassword) : void

重置密码 —— 第二步:跳转页设置新密码,重置后强制下线该账号其他会话。

流程:① 原子消费重置 token(读取即删除,并发重放仅一个成功) ② 再生盐并更新密码哈希(旧 salt 复用会让新旧哈希关联) ③ 强制下线:删 Redis token + 清库内 token_key + 清权限缓存 ④ 清理发送频控键

Parameters

string $token
string $newPassword

userinfo()

userinfo() : array

获取当前用户信息(含角色、权限与数据范围)。

流程:① 取当前登录用户 ② 装载角色 ID(注册表缓存)与权限(缓存服务) ③ 计算数据范围(豁免 = 全见,无需部门清单) ④ 组装返回(敏感字段由模型 $hidden 统一隐藏)

Returns

array —

changePassword()

changePassword(string  $oldPassword, string  $newPassword) : void

自助修改密码(个人中心)。

流程:① 校验当前密码(salt 增强哈希,失败 400 不暴露原因) ② 再生盐并更新密码哈希(旧 salt 复用会让新旧哈希关联,与 resetPassword 同规) ③ 记密码台账(CHANGE_TYPE_CHANGED,操作人=本人) ④ 强制全部下线(删 Redis token + 清库内 token_key + 清权限缓存)—— 与找回密码同规,防旧会话在 token 有效期内继续可用,前端收到后跳登录

Parameters

string $oldPassword
string $newPassword

Throws

\InvalidArgumentException

当前密码不正确

updateMyProfile()

updateMyProfile(array  $data) : void

自助编辑资料(个人中心)。

流程:① 白名单收敛(仅 real_name/email/phone/avatar,其余字段一律忽略) ② 空值归一化(文本字段存 '',与管理员编辑同规) ③ 邮箱/手机号唯一性 Service 层兜底(非空才参与、忽略自身,防绕过 Request) ④ 头像走文件库登记(存在/图片类型/可见性三查,与管理员编辑同规)

Parameters

array $data

Throws

\InvalidArgumentException

唯一冲突 / 头像参数无效

mySessions()

mySessions() : array

活跃会话列表(个人中心「会话管理」)。

Redis 会话键按前缀 SCAN(禁 KEYS),逐条解析 JWT 归属当前用户; 单会话模型下通常仅当前会话 1 条(登录会顶替旧会话),刷新轮换的 宽限期旧键已无 token 值自然跳过。会话标识脱敏(key 尾 8 位), 过期时间取自 JWT exp。

Returns

array —

revokeSession()

revokeSession(string  $md5Key) : void

下线指定会话(个人中心「会话管理」)。

流程:① 当前会话拒绝(走「退出登录」语义完整:清 cookie + 库锚点) ② 归属校验:仅允许下线本人活跃会话(防持他人会话标识强制下线) ③ 交由 revokeOneSession 完成吊销(JWT 拉黑 + 删键 + 清库锚点 + 登出日志)

Parameters

string $md5Key

Throws

\InvalidArgumentException

当前会话不可下线 / 会话不存在或已失效

revokeOtherSessions()

revokeOtherSessions() : int

批量下线全部非当前会话(个人中心「下线其他」)。

遍历当前用户活跃会话,跳过当前(走退出登录),其余逐个吊销; 返回实际下线数量(供界面反馈)。

Returns

int —

下线会话数

getMyPreferences()

getMyPreferences() : array

读取当前用户偏好(主题等账号维度设置;无显式偏好回落空数组)。

preference 经模型 casts array 序列化随 userinfo 下发, 独立接口供前端按需读写(免全量 userinfo 重拉)。

Returns

array —

updateMyPreferences()

updateMyPreferences(array  $data) : void

保存当前用户偏好(白名单收敛:仅 theme 一项,且取值在请求层已校验)。

合并语义:只更新传入的键,其余保留(防并发覆盖整包)。

Parameters

array $data

guard()

guard() : \Tymon\JWTAuth\JWTGuard

Returns

\Tymon\JWTAuth\JWTGuard —

ttlMinutes()

ttlMinutes(bool  $remember, int  $rememberDays = 1) : int

token 有效分钟数:登录与刷新共用同一逻辑,保证一致。 记住我 = 登录时选择的天数(remember_days × 1440); 否则 = 普通会话时长(配置 auth.session_ttl,默认 480 分钟 = 8 小时)。

Parameters

bool $remember
int $rememberDays

Returns

int —

notifyNewDeviceLogin()

notifyNewDeviceLogin(int  $userId) : void

新设备登录提醒(登录成功后调用)。

判定:浏览器指纹(User-Agent 哈希)作为 biz_key 的一部分,同一设备指纹 只提醒一次(唯一索引幂等,重复触发自动忽略)——不依赖登录日志查询, 无时序与环境依赖;开关 message.login_notify_enabled。

Parameters

int $userId

revokeOneSession()

revokeOneSession(\App\Models\SysUser  $user, string  $md5Key) : void

单个会话吊销(JWT 拉黑 + 删 Redis session/age 键 + 清库锚点 + 登出日志)。 「当前会话拒绝」由调用方(revokeSession)负责,本方法不做会话身份判断。

Parameters

\App\Models\SysUser $user
string $md5Key

normalizeMyAvatar()

normalizeMyAvatar(mixed  $avatar) : string

本人头像归一化(与管理员编辑同规):文件库登记 + 图片类型 + 可见性三查。

Parameters

mixed $avatar

Returns

string —

registerCodeTtl()

registerCodeTtl() : int

注册验证码有效期(秒):读配置 auth.register_code_ttl(分钟)

Returns

int —

registerSendInterval()

registerSendInterval() : int

验证码/注册邮件发送间隔(秒):读配置 auth.register_send_interval

Returns

int —

resetSendInterval()

resetSendInterval() : int

重置链接发送间隔(秒):读配置 auth.reset_send_interval(独立键,默认 60)

Returns

int —

registerCodeMaxAttempts()

registerCodeMaxAttempts() : int

验证码最大尝试次数:读配置 auth.register_code_attempts

Returns

int —

mailSendMax()

mailSendMax() : int

邮件发送次数上限:读配置 auth.mail_send_max(注册验证码/找回密码共用)

Returns

int —

mailSendLockSeconds()

mailSendLockSeconds() : int

邮件超限锁定(秒):读配置 auth.mail_send_lock_minutes(分钟)

Returns

int —

assertMailSendAllowed()

assertMailSendAllowed(string  $email) : ?string

邮件发送频控(注册验证码/找回密码共用):同一邮箱在锁定窗口内累计发送次数达到上限后锁定, 锁定期间拒绝发送,窗口过后计数自动重置恢复。返回 null 可发送;返回提示文案则被限流。

Parameters

string $email

Returns

?string —

resetTokenTtl()

resetTokenTtl() : int

重置链接有效期(秒):读配置 auth.reset_token_ttl(分钟)

Returns

int —

sessionAbsoluteMaxSeconds()

sessionAbsoluteMaxSeconds() : int

会话绝对生命周期上限(秒):读配置 auth.session_absolute_max_days,记住我也强制重登

Returns

int —

refreshRotationGraceSeconds()

refreshRotationGraceSeconds() : int

刷新轮换宽限期(秒):读配置 auth.refresh_rotation_grace_seconds

Returns

int —

registerEnabled()

registerEnabled() : bool

是否开放注册:读配置 auth.register_enabled(安全默认关闭——未配置即拒绝,防默认暴露注册面)

Returns

bool —

resetEnabled()

resetEnabled() : bool

是否开放找回密码:读配置 auth.reset_enabled(安全默认关闭——未配置即拒绝)

Returns

bool —

registerCodeKey()

registerCodeKey(string  $username, string  $email) : string

注册邮件验证码 key:md5(用户名+邮箱) 无分隔拼接。 用户名或邮箱任一变化即换 key → 旧验证码自然失效,必须重新获取。

Parameters

string $username
string $email

Returns

string —

resolveRegisterCode()

resolveRegisterCode(string  $username, string  $email) : array

取当前邮箱的注册验证码记录(hash),不存在或用户名不匹配视为已失效。

Parameters

string $username
string $email

Returns

array —

resolveResetToken()

resolveResetToken(string  $token) : ?array

按链接 token 反查缓存条目。

key 按 md5(邮箱) 定位、链接只携带随机 token,二者无直接映射, 故 SCAN user_repass_token_* 逐条比对值(认证流程条目量极少,30 分钟 TTL 自然清理)。

Parameters

string $token

Returns

?array —

consumeResetToken()

consumeResetToken(string  $token) : ?array

原子消费重置 token:读取即删除(getdel),单次使用语义在 Redis 层闭合—— 并发重放下仅一个请求拿到值,其余视为已消费。

Parameters

string $token

Returns

?array —

verifyCaptcha()

verifyCaptcha(string  $key, array  $captcha, string|null  $expectedKey = null) : void

统一验证码校验(点选坐标)。

Parameters

string $key
array $captcha
string|null $expectedKey

业务维度预期 key(如 user_login_captcha_{md5(账号)});非 null 时校验提交 key 与之匹配, 防止「用自己账号取的验证码去爆破他人账号」