diff --git a/Server/application/common/config/route.php b/Server/application/common/config/route.php index c7f823d8..8b133a1a 100644 --- a/Server/application/common/config/route.php +++ b/Server/application/common/config/route.php @@ -6,17 +6,17 @@ use think\facade\Route; // 定义RESTful风格的API路由 - 认证相关 Route::group('v1/auth', function () { // 无需认证的接口 - Route::post('login', 'app\\common\\controller\\Auth@login'); // 账号密码登录 - Route::post('mobile-login', 'app\\common\\controller\\Auth@mobileLogin'); // 手机号验证码登录 - Route::post('code', 'app\\common\\controller\\Auth@sendCode'); // 发送验证码 + Route::post('login', 'app\common\controller\PasswordLoginController@index'); // 账号密码登录 + Route::post('mobile-login', 'app\common\controller\Auth@mobileLogin'); // 手机号验证码登录 + Route::post('code', 'app\common\controller\Auth@SendCodeController'); // 发送验证码 // 需要JWT认证的接口 - Route::get('info', 'app\\common\\controller\\Auth@info')->middleware(['jwt']); // 获取用户信息 - Route::post('refresh', 'app\\common\\controller\\Auth@refresh')->middleware(['jwt']); // 刷新令牌 + Route::get('info', 'app\common\controller\Auth@info')->middleware(['jwt']); // 获取用户信息 + Route::post('refresh', 'app\common\controller\Auth@refresh')->middleware(['jwt']); // 刷新令牌 }); // 附件上传相关路由 Route::group('v1/', function () { - Route::post('attachment/upload', 'app\\common\\controller\\Attachment@upload'); // 上传附件 - Route::get('attachment/:id', 'app\\common\\controller\\Attachment@info'); // 获取附件信息 + Route::post('attachment/upload', 'app\common\controller\Attachment@upload'); // 上传附件 + Route::get('attachment/:id', 'app\common\controller\Attachment@info'); // 获取附件信息 })->middleware(['jwt']); \ No newline at end of file diff --git a/Server/application/common/controller/BaseController.php b/Server/application/common/controller/BaseController.php new file mode 100644 index 00000000..81dcf83c --- /dev/null +++ b/Server/application/common/controller/BaseController.php @@ -0,0 +1,12 @@ +where('phone', $account)->whereOr('account', $account); + }) + ->where(function ($query) use ($typeId) { + $query->where('status', 1)->where('typeId', $typeId); + })->find(); + + return $user; + } + + /** + * 获取用户信息 + * + * @param string $account 账号(手机号) + * @param string $password 密码(可能是加密后的) + * @param int $typeId 身份信息 + * @return array|null + */ + protected function getUser(string $account, string $password, int $typeId): array + { + $user = $this->getUserProfileWithAccountAndType($account, $typeId); + + if (!$user) { + throw new \Exception('用户不存在或已禁用', 403); + } + + if ($user->passwordMd5 !== md5($password)) { + throw new \Exception('账号或密码错误', 403); + } + + return $user->toArray(); + } + + /** + * 数据验证 + * + * @param array $params + * @return $this + * @throws \Exception + */ + protected function dataValidate(array $params): self + { + $validate = Validate::make([ + 'account' => 'require', + 'password' => 'require|length:6,64', + 'typeId' => 'require|in:1,2', + ], [ + 'account.require' => '账号不能为空', + 'password.require' => '密码不能为空', + 'password.length' => '密码长度必须在6-64个字符之间', + 'typeId.require' => '用户类型不能为空', + 'typeId.in' => '用户类型错误', + ]); + + if (!$validate->check($params)) { + throw new \Exception($validate->getError(), 400); + } + + return $this; + } + + /** + * 用户登录 + * + * @param string $account 账号(手机号) + * @param string $password 密码(可能是加密后的) + * @param string $typeId 登录IP + * @return array + * @throws \Exception + */ + protected function doLogin(string $account, string $password, int $typeId): array + { + // 获取用户信息 + $member = $this->getUser($account, $password, $typeId); + + // 生成JWT令牌 + $token = JwtUtil::createToken($member, 7200); + $token_expired = time() + 7200; + + return compact('member', 'token', 'token_expired'); + } + + /** + * 用户登录 + * + * @return Json + */ + public function index() + { + $params = $this->request->only(['account', 'password', 'typeId']); + + try { + $result = $this->dataValidate($params)->doLogin( + $params['account'], + $params['password'], + $params['typeId'] + ); + + return ResponseHelper::success($result, '登录成功'); + } catch (Exception $e) { + return ResponseHelper::error($e->getMessage(), $e->getCode()); + } + } +} \ No newline at end of file diff --git a/Server/application/common/controller/SendCodeController.php b/Server/application/common/controller/SendCodeController.php new file mode 100644 index 00000000..7dc58d23 --- /dev/null +++ b/Server/application/common/controller/SendCodeController.php @@ -0,0 +1,39 @@ +request->only(['account', 'type']); + + // 参数验证 + $validate = validate('common/Auth'); + if (!$validate->scene('send_code')->check($params)) { + return ResponseHelper::error($validate->getError()); + } + + try { + // 调用发送验证码服务 + $result = $this->authService->sendLoginCode( + $params['account'], + $params['type'] + ); + return ResponseHelper::success($result, '验证码发送成功'); + } catch (\Exception $e) { + return ResponseHelper::error($e->getMessage()); + } + } +} \ No newline at end of file diff --git a/Server/application/common/model/User.php b/Server/application/common/model/User.php index bd37c17f..c3535d5c 100644 --- a/Server/application/common/model/User.php +++ b/Server/application/common/model/User.php @@ -1,4 +1,5 @@ 'integer', - 'isAdmin' => 'integer', - 'companyId' => 'integer', - 'typeId' => 'integer', - 'lastLoginTime' => 'integer', - 'status' => 'integer', - 'createTime' => 'integer', - 'updateTime' => 'integer', - 'deleteTime' => 'integer' - ]; - - /** - * 通过手机号获取用户信息 - * @param string $account 手机号 - * @return array|null - */ - public static function getUserByMobile($account) - { - // 查询用户 - $user = self::where('account', $account) - ->where('status', 1) - ->find(); - - if (!$user) { - return null; - } - - // 用手机号当做默认用户名(如果没有设置用户名) - $username = $user->username ?: $user->account; - // 默认头像地址 - $avatar = $user->avatar ?: ''; - - return [ - 'id' => $user->id, - 'username' => $username, - 'account' => $user->account, - 'avatar' => $avatar, - 'isAdmin' => $user->isAdmin, - 'companyId' => $user->companyId, - 'typeId' => $user->typeId, - 'lastLoginIp' => $user->lastLoginIp, - 'lastLoginTime' => $user->lastLoginTime - ]; - } } \ No newline at end of file diff --git a/Server/application/common/service/AuthService.php b/Server/application/common/service/AuthService.php index b94c57cc..a2eac90e 100644 --- a/Server/application/common/service/AuthService.php +++ b/Server/application/common/service/AuthService.php @@ -1,85 +1,59 @@ where('phone', $account)->whereOr('account', $account); + }) + ->where(function ($query) use ($typeId) { + $query->where('status', 1)->where('typeId', $typeId); + })->find(); + + return $user; + } + /** * 获取用户信息 + * * @param string $account 账号(手机号) * @param string $password 密码(可能是加密后的) * @param int $typeId 身份信息 * @return array|null */ - protected function getUser($account, $password, $typeId) + protected function getUser(string $account, string $password, int $typeId): array { - // 查询用户 - $user = User::where('account', $account) - ->where('typeId', $typeId) - ->where('status', 1) - ->find(); + $user = $this->getUserProfileWithAccountAndType($account, $typeId); if (!$user) { - // 记录日志 - \think\facade\Log::info('用户不存在或已禁用', ['account' => $account]); - return null; + throw new \Exception('用户不存在或已禁用', 403); } - // 记录密码验证信息 - \think\facade\Log::info('密码验证', [ - 'account' => $account, - 'input_password' => $password, - 'stored_hash' => $user->passwordMd5, - ]); - - // 验证密码 - $isValid = ($user->passwordMd5 == md5($password)); - - \think\facade\Log::info('密码验证结果', [ - 'account' => $account, - 'is_valid' => $isValid, - ]); - - if (!$isValid) { - return null; + if ($user->passwordMd5 !== md5($password)) { + throw new \Exception('账号或密码错误', 403); } - // 更新登录信息 - $user->lastLoginIp = request()->ip(); - $user->lastLoginTime = time(); - $user->save(); - - // 用手机号当做默认用户名(如果没有设置用户名) - $username = $user->username ?: $user->account; - - return [ - 'id' => $user->id, - 'username' => $username, - 'account' => $user->account, - 'avatar' => $user->avatar, - 'isAdmin' => $user->isAdmin, - 'companyId' => $user->companyId, - 'typeId' => $user->typeId, - 'lastLoginIp' => $user->lastLoginIp, - 'lastLoginTime' => $user->lastLoginTime - ]; + return $user->toArray(); } /** @@ -92,39 +66,28 @@ class AuthService /** * 用户登录 + * * @param string $account 账号(手机号) * @param string $password 密码(可能是加密后的) * @param string $ip 登录IP * @return array * @throws \Exception */ - public function login($account, $password, $typeId, $ip) + public function login(string $account, string $password, int $typeId, string $ip) { // 获取用户信息 - $user = $this->getUser($account, $password, $typeId); + $member = $this->getUser($account, $password, $typeId); - if (empty($user)) { - // 记录登录失败 - Log::info('登录失败', ['account' => $account, 'ip' => $ip, 'is_encrypted' => true]); - throw new \Exception('账号或密码错误'); - } - // 生成JWT令牌 $token = JwtUtil::createToken($user, self::TOKEN_EXPIRE); - $expireTime = time() + self::TOKEN_EXPIRE; - - // 记录登录成功 - Log::info('登录成功', ['account' => $account, 'ip' => $ip]); - - return [ - 'token' => $token, - 'token_expired' => $expireTime, - 'member' => $user - ]; + $token_expired = time() + self::TOKEN_EXPIRE; + + return compact('member', 'token', 'token_expired'); } /** * 手机号验证码登录 + * * @param string $account 手机号 * @param string $code 验证码(可能是加密后的) * @param string $ip 登录IP @@ -137,14 +100,14 @@ class AuthService // 验证验证码 if (!$this->smsService->verifyCode($account, $code, 'login', $isEncrypted)) { Log::info('验证码验证失败', ['account' => $account, 'ip' => $ip, 'is_encrypted' => $isEncrypted]); - throw new \Exception('验证码错误或已过期'); + throw new \Exception('验证码错误或已过期', 404); } // 获取用户信息 $user = User::getUserByMobile($account); if (empty($user)) { Log::info('用户不存在', ['account' => $account, 'ip' => $ip]); - throw new \Exception('用户不存在'); + throw new \Exception('用户不存在', 404); } // 生成JWT令牌 @@ -163,6 +126,7 @@ class AuthService /** * 发送登录验证码 + * * @param string $account 手机号 * @param string $type 验证码类型 * @return array @@ -175,6 +139,7 @@ class AuthService /** * 获取用户信息 + * * @param array $userInfo JWT中的用户信息 * @return array * @throws \Exception @@ -184,16 +149,17 @@ class AuthService if (empty($userInfo)) { throw new \Exception('获取用户信息失败'); } - + // 移除不需要返回的字段 unset($userInfo['exp']); unset($userInfo['iat']); - + return $userInfo; } /** * 刷新令牌 + * * @param array $userInfo JWT中的用户信息 * @return array * @throws \Exception @@ -203,15 +169,15 @@ class AuthService if (empty($userInfo)) { throw new \Exception('刷新令牌失败'); } - + // 移除过期时间信息 unset($userInfo['exp']); unset($userInfo['iat']); - + // 生成新令牌 $token = JwtUtil::createToken($userInfo, self::TOKEN_EXPIRE); $expireTime = time() + self::TOKEN_EXPIRE; - + return [ 'token' => $token, 'token_expired' => $expireTime @@ -220,52 +186,53 @@ class AuthService /** * 获取系统授权信息,使用缓存存储10分钟 + * * @return string */ public static function getSystemAuthorization() { // 定义缓存键名 $cacheKey = 'system_authorization_token'; - + // 尝试从缓存获取授权信息 $authorization = Cache::get($cacheKey); //$authorization = 'mYpVVhPY7PxctvYw1pn1VCTS2ck0yZG8q11gAiJrRN_D3q7KXXBPAfXoAmqs7kKHeaAx-h4GB7DiqVIQJ09HiXVhaQT6PtgLX3w8YV16erThC-lG1fyJB4DJxu-QxA3Q8ogSs1WFOa8aAXD1QQUZ7Kbjkw_VMLL4lrfe0Yjaqy3DnO7aL1xGnNjjX8P5uqCAZgHKlN8NjuDEGyYvXygW1YyoK9pNpwvq-6DYKjLWdmbHvFaAybHf-hU1XyrFavZqcZYxIoVXjfJ5ASp4XxeCWqMCzwtSoz9RAvwLAlNxGweowtuyX9389ZaXI-zbqb2T0S8llg'; - // 如果缓存中没有或已过期,则重新获取 + // 如果缓存中没有或已过期,则重新获取 if (empty($authorization)) { try { // 从环境变量中获取API用户名和密码 $username = Env::get('api.username', ''); $password = Env::get('api.password', ''); - + if (empty($username) || empty($password)) { Log::error('缺少API用户名或密码配置'); return ''; } - + // 构建登录参数 $params = [ 'grant_type' => 'password', 'username' => $username, 'password' => $password ]; - + // 获取API基础URL $baseUrl = Env::get('api.wechat_url', ''); if (empty($baseUrl)) { Log::error('缺少API基础URL配置'); return ''; } - + // 调用登录接口获取token // 设置请求头 $headerData = ['client:system']; $header = setHeader($headerData, '', 'plain'); - $result = requestCurl($baseUrl . 'token', $params, 'POST',$header); + $result = requestCurl($baseUrl . 'token', $params, 'POST', $header); $result_array = handleApiResponse($result); if (isset($result_array['access_token']) && !empty($result_array['access_token'])) { $authorization = $result_array['access_token']; - + // 存入缓存,有效期10分钟(600秒) Cache::set($cacheKey, $authorization, 600); Cache::set('system_refresh_token', $result_array['refresh_token'], 600); @@ -281,7 +248,7 @@ class AuthService return ''; } } - + return $authorization; } } \ No newline at end of file diff --git a/Server/application/common/service/SmsService.php b/Server/application/common/service/SmsService.php index 91d08773..e0552f11 100644 --- a/Server/application/common/service/SmsService.php +++ b/Server/application/common/service/SmsService.php @@ -1,4 +1,5 @@ checkSendLimit($mobile, $type); - + // 生成验证码 $code = $this->generateCode(); - + // 缓存验证码 $this->saveCode($mobile, $code, $type); - + // 发送验证码(实际项目中对接短信平台) $this->doSend($mobile, $code, $type); - + // 记录日志 Log::info('发送验证码', [ 'mobile' => $mobile, 'type' => $type, 'code' => $code ]); - + return [ 'mobile' => $mobile, 'expire' => self::CODE_EXPIRE, @@ -54,7 +55,7 @@ class SmsService 'code' => $code ]; } - + /** * 验证验证码 * @param string $mobile 手机号 @@ -67,7 +68,7 @@ class SmsService { $cacheKey = $this->getCodeCacheKey($mobile, $type); $cacheCode = Cache::get($cacheKey); - + if (!$cacheCode) { Log::info('验证码不存在或已过期', [ 'mobile' => $mobile, @@ -75,15 +76,15 @@ class SmsService ]); return false; } - + // 验证码是否匹配 $isValid = false; - + if ($isEncrypted) { // 前端已加密,需要对缓存中的验证码进行相同的加密处理 $encryptedCacheCode = $this->encryptCode($cacheCode); $isValid = hash_equals($encryptedCacheCode, $code); - + // 记录日志 Log::info('加密验证码验证', [ 'mobile' => $mobile, @@ -95,7 +96,7 @@ class SmsService } else { // 未加密,直接比较 $isValid = ($cacheCode === $code); - + // 记录日志 Log::info('明文验证码验证', [ 'mobile' => $mobile, @@ -104,15 +105,15 @@ class SmsService 'is_valid' => $isValid ]); } - + // 验证成功后删除缓存 if ($isValid) { Cache::rm($cacheKey); } - + return $isValid; } - + /** * 检查发送频率限制 * @param string $mobile 手机号 @@ -122,24 +123,24 @@ class SmsService protected function checkSendLimit($mobile, $type) { $cacheKey = $this->getCodeCacheKey($mobile, $type); - + // 检查是否存在未过期的验证码 if (Cache::has($cacheKey)) { throw new \Exception('验证码已发送,请稍后再试'); } - + // 检查当日发送次数限制 $limitKey = "sms_limit:{$mobile}:" . date('Ymd'); $sendCount = Cache::get($limitKey, 0); - + if ($sendCount >= 10) { throw new \Exception('今日发送次数已达上限'); } - + // 更新发送次数 Cache::set($limitKey, $sendCount + 1, 86400); } - + /** * 生成随机验证码 * @return string @@ -149,7 +150,7 @@ class SmsService // 生成4位数字验证码 return sprintf("%0" . self::CODE_LENGTH . "d", mt_rand(0, pow(10, self::CODE_LENGTH) - 1)); } - + /** * 保存验证码到缓存 * @param string $mobile 手机号 @@ -161,7 +162,7 @@ class SmsService $cacheKey = $this->getCodeCacheKey($mobile, $type); Cache::set($cacheKey, $code, self::CODE_EXPIRE); } - + /** * 执行发送验证码 * @param string $mobile 手机号 @@ -175,7 +176,7 @@ class SmsService // 这里仅做模拟,返回成功 return true; } - + /** * 获取验证码缓存键名 * @param string $mobile 手机号 @@ -186,7 +187,7 @@ class SmsService { return "sms_code:{$mobile}:{$type}"; } - + /** * 加密验证码 * 使用与前端相同的加密算法 diff --git a/Server/application/cunkebao/controller/BaseController.php b/Server/application/cunkebao/controller/BaseController.php index 1189cafa..852858f9 100644 --- a/Server/application/cunkebao/controller/BaseController.php +++ b/Server/application/cunkebao/controller/BaseController.php @@ -32,7 +32,7 @@ class BaseController extends Controller * @return mixed * @throws \Exception */ - protected function getUserInfo(string $column = '') + protected function getUserInfo(?string $column = null) { $user = $this->request->userInfo;