validate([ 'code' => ['bail', 'required', 'string'], ]); $code = $input['code']; //获取第三方用户信息 $socialite = $this->getSocialiteUserByCode($provider, $code); //通过第三方用户信息登录已绑定账号 $token = null; $socialiteUser = SocialiteUser::firstOrCreate([ 'socialite_type' => $provider, 'socialite_id' => $socialite?->id, ]); $user = $socialiteUser->user; if ($user) { $token = $this->loginUser($user, $request); } return response()->json([ 'token' => $token?->plainTextToken, ]); } public function codeBindUser($provider, Request $request) { $type = $request->input('type', 'default'); $rules = [ 'code' => ['bail', 'required', 'string'], 'inviter_code' => ['bail', 'nullable', 'string'], ]; switch ($type) { case 'default'://手机号+密码 $rules = array_merge($rules, [ 'phone' => ['bail', 'required', 'string'], 'password' => ['bail', 'required', 'string'], ]); break; case 'sms-code'://手机号+验证码 $rules = array_merge($rules, [ 'phone' => ['bail', 'required', new PhoneNumberRule()], 'verify_code' => ['bail', 'required', 'string'], ]); break; case 'wechat-mini'://微信小程序解密手机号 $rules = array_merge($rules, [ 'data' => ['bail', 'required', 'string'], 'iv' => ['bail', 'required', 'string'], ]); break; default://默认手机号+密码 $rules = array_merge($rules, [ 'phone' => ['bail', 'required', 'string'], 'password' => ['bail', 'required', 'string'], ]); break; } $input = $request->validate($rules); $code = $input['code']; //获取第三方用户信息 $socialite = $this->getSocialiteUserByCode($provider, $code); //绑定用户,并返回token $token = $this->bindUser([ 'socialite_type'=>$provider, 'socialite_id'=>$socialite?->id, ], $type ?? 'default', $request); return response()->json([ 'token' => $token?->plainTextToken, ]); } /** * 第三方登录的解绑 * * @param [type] $provider * @param Request $request */ public function unbindUser($provider, Request $request) { if ($user = $request->user()) { //解绑三方的关系 SocialiteUser::where([ 'user_id' => $user->id, 'socialite_type' => $provider, ])->update([ 'user_id' => null, ]); } return response()->noContent(); } /** * [目前支持:微信小程序] */ protected function getSocialiteUserByCode($provider, $code) { //获取第三方用户信息 $user = null; $config = config('socialite', []); $socialite = new SocialiteManager($config); switch ($provider) { case SocialiteType::WechatMiniProgram->value: //微信小程序 $user = $socialite->create(SocialiteType::WechatMiniProgram->value)->userFromCode($code); break; default: throw new BizException(404); } return $user; } /** * 第三方绑定用户 * * @param [array] $socialite * @param [Request] $request */ protected function bindUser(array $socialite, string $type, Request $request) { $token = null; $socialiteUser = SocialiteUser::firstOrCreate($socialite); $user = null; $input = $request->input(); $phone = $input['phone'] ?? ''; switch ($type) { case 'default'://手机号+密码 $user = User::where('phone', $phone)->first(); //手机号不存在,或者密码错误 if (! $user?->verifyPassword($input['password'])) { throw new BizException(__('Incorrect account or password')); } break; case 'sms-code'://手机号+验证码 app(SmsCodeService::class)->validate( $input['phone'], SmsCode::TYPE_REGISTER, $input['verify_code'] ); $user = User::where('phone', $phone)->first(); break; case 'wechat-mini'://微信小程序解密手机号 //解密失败 $app = EasyWeChatFactory::miniProgram([ 'app_id' => config('wechat.mini_program.default.app_id', ''), 'secret' => config('wechat.mini_program.default.secret', ''), // 下面为可选项 // 指定 API 调用返回结果的类型:array(default)/collection/object/raw/自定义类名 'response_type' => 'array', 'log' => [ 'level' => 'debug', 'file' => storage_path('logs/wechat-mini.log'), ], ]); $session = Cache::get($socialite['socialite_id']); try { $decryptedData = $app->encryptor->decryptData($session, $input['iv'], $input['data']); } catch (\EasyWeChat\Kernel\Exceptions\DecryptException $e) { report($e); throw new BizException('系统错误, 请重新进入小程序'); } $phone = data_get($decryptedData, 'phoneNumber'); //解密成功,$user $user = User::where('phone', $phone)->first(); break; } if (empty($phone)) { throw new BizException('系统错误,未找到手机号'); } //走登录逻辑 if ($user) { $token = $this->loginUser($user, $request); } else {//走注册逻辑 $time = now(); $ip = $request->realIp(); $inviter = $this->findUserByCode((string) Arr::get($input, 'inviter_code')); try { DB::beginTransaction(); $user = User::create([ 'phone' => $phone, 'password' => Str::random(6), //先随机一个密码 'phone_verified_at' => $time, 'register_ip' => $ip, 'last_login_at' => $time, 'last_login_ip' => $ip, ], $inviter ); DB::commit(); } catch (Throwable $e) { DB::rollBack(); report($e); throw new BizException(__('Registration failed, please try again')); } $token = $user->createToken(Device::UNIAPP, ['mall']); } //解绑对应三方以前的关系 SocialiteUser::where([ 'user_id' => $user->id, 'socialite_type' => $socialite['socialite_type'], ])->update([ 'user_id' => null, ]); //绑定用户和三方信息关系 $socialiteUser->update([ 'user_id' => $user->id, ]); return $token; } /** * 第三方登录现有绑定的用户 * * @param [User] $user * @param [Request] $request */ protected function loginUser(User $user, Request $request) { $token = null; $user->last_login_at = now(); $user->last_login_ip = $request->realIp(); $user->save(); // 获取登录设备 $device = $request->header('client-app', Device::UNIAPP); $device = Device::UNIAPP; // 清理此用户的商城端令牌 $user->tokens()->where('name', $device)->delete(); // 颁发新的商城端令牌 $token = $user->createToken($device, ['mall']); return $token; } /** * 通过邀请码搜索用户 * * @param string $code * @return \App\Models\User|null * * @throws \App\Exceptions\BizException */ protected function findUserByCode(string $code): ?User { if ($code === '') { return null; } $user = User::when(PhoneNumber::validate($code), function ($query) use ($code) { $query->where('phone', $code); }, function ($query) use ($code) { $query->whereRelation('userInfo', 'code', $code); })->first(); if ($user === null) { throw new BizException(__('Inviter does not exist')); } return $user; } }