6
0
Fork 0
jiqu-library-server/app/Endpoint/Api/Http/Controllers/Auth/SocialiteAuthController.php

335 lines
12 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace App\Endpoint\Api\Http\Controllers\Auth;
use App\Constants\Device;
use App\Endpoint\Api\Http\Controllers\Controller;
use App\Exceptions\BizException;
use App\Helpers\PhoneNumber;
use App\Models\SmsCode;
use App\Models\SocialiteUser;
use App\Models\User;
use App\Models\UserInfo;
use App\Rules\PhoneNumber as PhoneNumberRule;
use App\Services\SmsCodeService;
use EasyWeChat\Factory as EasyWeChatFactory;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Overtrue\Socialite\SocialiteManager;
use Throwable;
class SocialiteAuthController extends Controller
{
/**
* 三方code登录
*/
public function codeAuth($provider, Request $request)
{
$input = $request->validate([
'code' => ['bail', 'required', 'string'],
]);
$code = $input['code'];
//获取第三方用户信息
$socialiteUser = $this->getSocialiteUserByCode($provider, $code);
//通过第三方用户信息登录已绑定账号
$token = $this->loginUser([
'socialite_type'=>$provider,
'socialite_id'=>$socialiteUser?->id,
], $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'];
//获取第三方用户信息
$socialiteUser = $this->getSocialiteUserByCode($provider, $code);
//绑定用户并返回token
$token = $this->bindUser([
'socialite_type'=>$provider,
'socialite_id'=>$socialiteUser?->id,
], $type ?? 'default', $request);
return response()->json([
'token' => $token?->plainTextToken,
]);
}
/**
* [目前支持:微信小程序]
*/
protected function getSocialiteUserByCode($provider, $code)
{
//获取第三方用户信息
$user = null;
$config = config('socialite', []);
$socialite = new SocialiteManager($config);
switch ($provider) {
case 'wechat-mini'://微信小程序
$user = $socialite->create('wehcat-mini')->userFromCode($code);
break;
default:
throw new BizException(404);
}
return $user;
}
/**
* 第三方登录现有绑定的用户
*
* @param [array] $socialite
* @param [Request] $request
*/
protected function loginUser(array $socialite, Request $request)
{
$token = null;
$socialiteUser = SocialiteUser::firstOrCreate($socialite);
$user = $socialiteUser->user;
if ($user) {
$user->last_login_at = now();
$user->last_login_ip = $request->realIp();
$user->save();
// 获取登录设备
$device = $request->header('client-app', Device::UNIAPP);
switch ($device) {
case Device::MERCHANT:
if ($user->userInfo?->agent_level < UserInfo::AGENT_LEVEL_VIP) {
throw new BizException('账户没有权限');
}
// 清理此用户的商户端令牌
$user->tokens()->where('name', $device)->delete();
// 颁发新的商户端令牌
$token = $user->createToken($device);
break;
case Device::DEALER:
if (!$user->isDealer()) {
throw new BizException('账户没有权限');
}
// 清理此用户的商户端令牌
$user->tokens()->where('name', $device)->delete();
// 颁发新的商户端令牌
$token = $user->createToken($device);
break;
default:
$device = Device::UNIAPP;
// 清理此用户的商城端令牌
$user->tokens()->where('name', $device)->delete();
// 颁发新的商城端令牌
$token = $user->createToken($device, ['mall']);
break;
}
}
return $token;
}
protected function bindUser(array $socialite, string $type, Request $request)
{
$token = null;
$socialiteUser = SocialiteUser::firstOrCreate($socialite);
$user = null;
$input = $request->input();
switch ($type) {
case 'default'://手机号+密码
$user = User::where('phone', $input['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', $input['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) {
return $this->error('系统错误, 请重新进入小程序');
}
$phone = data_get($decryptedData, 'phoneNumber');
//解密成功,$user
$user = User::where('phone', $phone)->first();
break;
}
//走登录逻辑
if ($user) {
$user->last_login_at = now();
$user->last_login_ip = $request->realIp();
$user->save();
// 获取登录设备
$device = $request->header('client-app', Device::UNIAPP);
switch ($device) {
case Device::MERCHANT:
if ($user->userInfo?->agent_level < UserInfo::AGENT_LEVEL_VIP) {
throw new BizException('账户没有权限');
}
// 清理此用户的商户端令牌
$user->tokens()->where('name', $device)->delete();
// 颁发新的商户端令牌
$token = $user->createToken($device);
break;
case Device::DEALER:
if (!$user->isDealer()) {
throw new BizException('账户没有权限');
}
// 清理此用户的商户端令牌
$user->tokens()->where('name', $device)->delete();
// 颁发新的商户端令牌
$token = $user->createToken($device);
break;
default:
$device = Device::UNIAPP;
// 清理此用户的商城端令牌
$user->tokens()->where('name', $device)->delete();
// 颁发新的商城端令牌
$token = $user->createToken($device, ['mall']);
break;
}
} else {//走注册逻辑
$time = now();
$ip = $request->realIp();
$inviter = $this->findUserByCode((string) Arr::get($input, 'code'));
try {
DB::beginTransaction();
$user = User::create(
array_merge($input, [
'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'));
}
// 获取登录设备
$device = $request->header('client-app', Device::UNIAPP);
switch ($device) {
case Device::DEALER:
$token = $user->createToken(Device::DEALER);
break;
default:
$token = $user->createToken(Device::UNIAPP, ['mall']);
break;
}
}
//解绑以前的关系
SocialiteUser::where('user_id', $user->id)->update([
'user_id' => null,
]);
//绑定用户和三方信息关系
$socialiteUser->update([
'user_id' => $user->id,
]);
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;
}
}