salt); $user = DB::table('mp_manage_users')->where('account', $account)->where('pwd', $pwd)->where('is_deleted', 0)->first(); if (!$user) { Utils::throwError('20003:用户不存在'); } if ((int)getProp($user, 'is_enabled') === 0) { Utils::throwError('20003:用户已被禁用'); } // 更新 token、token 过期时间与 refresh_token $uid = getProp($user, 'id'); $nickname = getProp($user, 'nickname'); $token = (string)getProp($user, 'token'); $refreshToken = (string)getProp($user, 'refresh_token'); if (!config('token.expire_check_enabled')) { // 开关关闭:仅依据 token 是否存在处理,不写入 token 过期时间与 refresh_token 相关字段 if ($token === '') { $token = md5(makeRandStr(32)); DB::table('mp_manage_users')->where('id', $uid)->update([ 'token' => $token, 'updated_at' => date('Y-m-d H:i:s'), ]); } $tokenData = $this->currentTokenData($user); $tokenData['token'] = $token; } elseif ($token !== '' && $refreshToken !== '' && !$this->isExpired(getProp($user, 'token_expires_at')) && !$this->isExpired(getProp($user, 'refresh_token_expires_at'))) { // 已有可用登录态:重新登录不刷新 token 信息(多人协作共用一个账号时避免互相踢下线) $tokenData = $this->currentTokenData($user); } else { // 首次登录、老用户只有 token 没有 refresh_token、登录态已过期:补齐 token 过期时间与 refresh_token 相关字段 // token 未过期时沿用原 token,避免多人协作场景下其余人掉线 $keepToken = $token !== '' && !$this->isExpired(getProp($user, 'token_expires_at')) ? $token : ''; $tokenData = $this->buildTokenData($keepToken); DB::table('mp_manage_users')->where('id', $uid)->update([ 'token' => $tokenData['token'], 'token_expires_at' => $tokenData['token_expires_at'], 'refresh_token' => $tokenData['refresh_token'], 'refresh_token_expires_at' => $tokenData['refresh_token_expires_at'], 'updated_at' => date('Y-m-d H:i:s'), ]); } // 用户角色 $role = getProp($user, 'role'); return array_merge(compact('uid', 'nickname', 'role'), $tokenData); } public function userInfo(): array { $uid = Site::getUid(); $user = DB::table('mp_manage_users')->where('id', $uid)->where('is_deleted', 0)->first(); if (!$user) { Utils::throwError('20003:用户不存在'); } if ((int)getProp($user, 'is_enabled') === 0) { Utils::throwError('20003:用户已被禁用'); } // 更新token $uid = getProp($user, 'id'); $nickname = getProp($user, 'nickname'); $token = getProp($user, 'token'); $token_expires_at = getProp($user, 'token_expires_at'); if ($token_expires_at) $token_expires_at = transDate($token_expires_at); $refresh_token = getProp($user, 'refresh_token'); $refresh_token_expires_at = getProp($user, 'refresh_token_expires_at'); if ($refresh_token_expires_at) $refresh_token_expires_at = transDate($refresh_token_expires_at); // 用户角色 $role = getProp($user, 'role'); $points = getProp($user, 'points'); $avatar = getProp($user, 'avatar'); return compact('uid', 'nickname', 'avatar', 'token', 'token_expires_at', 'refresh_token', 'refresh_token_expires_at', 'role', 'points'); } /** * 通过 refresh_token 刷新 token 与 refresh_token * * @param string $refreshToken * @return array */ public function refreshToken($refreshToken): array { $refreshToken = trim((string)$refreshToken); if ($refreshToken === '') { Utils::throwError('1003:请传入refresh_token'); } $user = DB::table('mp_manage_users')->where('refresh_token', $refreshToken)->where('is_deleted', 0)->first(); if (!$user) { Utils::throwError(ErrorConst::TOKEN_EXPIRED); } if ((int)getProp($user, 'is_enabled') === 0) { Utils::throwError('20003:用户已被禁用'); } // refresh_token 自身过期则不可续期,需重新登录 $refreshTokenExpiresAt = getProp($user, 'refresh_token_expires_at'); if ($refreshTokenExpiresAt && strtotime($refreshTokenExpiresAt) <= time()) { Utils::throwError(ErrorConst::TOKEN_EXPIRED); } $uid = (int)getProp($user, 'id'); $nickname = getProp($user, 'nickname'); $role = getProp($user, 'role'); // 刷新时强制更换 token 与 refresh_token $tokenData = $this->buildTokenData(); DB::table('mp_manage_users')->where('id', $uid)->update([ 'token' => $tokenData['token'], 'token_expires_at' => $tokenData['token_expires_at'], 'refresh_token' => $tokenData['refresh_token'], 'refresh_token_expires_at' => $tokenData['refresh_token_expires_at'], 'updated_at' => date('Y-m-d H:i:s'), ]); return array_merge(compact('uid', 'nickname', 'role'), $tokenData); } /** * 生成 token、refresh_token 及其过期时间 * * @param string $token 需要沿用的 token(传空则重新生成) * @return array */ private function buildTokenData(string $token = ''): array { if ($token === '') { $token = md5(makeRandStr(32)); } return [ 'token' => $token, 'token_expires_at' => date('Y-m-d H:i:s', time() + (int)config('token.expire_seconds', 864000)), 'refresh_token' => md5(makeRandStr(32)), 'refresh_token_expires_at' => date('Y-m-d H:i:s', time() + (int)config('token.refresh_expire_seconds', 2592000)), ]; } /** * 读取当前登录态(不做任何刷新) * * @param object $user 用户记录 * @return array */ private function currentTokenData($user): array { return [ 'token' => (string)getProp($user, 'token'), 'token_expires_at' => transDate(getProp($user, 'token_expires_at')), 'refresh_token' => (string)getProp($user, 'refresh_token'), 'refresh_token_expires_at' => transDate(getProp($user, 'refresh_token_expires_at')), ]; } /** * 时间是否已过期(空值视为未过期,兼容历史数据) * * @param mixed $expiresAt * @return bool */ private function isExpired($expiresAt): bool { return (bool)($expiresAt && strtotime($expiresAt) <= time()); } /** * 退出登录 * * @param $token * @return bool */ public function logout($token): bool { if (empty($token)) { return true; } // 退出不影响多设备 return DB::table('mp_manage_users')->where('token', $token)->update([ 'updated_at' => date('Y-m-d H:i:s') ]); } }