test_lgq/niucloud/app/service/api/login/LoginService.php
2024-01-24 17:36:08 +08:00

303 lines
10 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的saas管理平台
// +----------------------------------------------------------------------
// | 官方网址https://www.niucloud-admin.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\service\api\login;
use app\dict\member\MemberLoginTypeDict;
use app\dict\sys\AppTypeDict;
use app\dict\sys\SmsDict;
use app\model\member\Member;
use app\service\api\captcha\CaptchaService;
use app\service\api\member\MemberConfigService;
use app\service\api\member\MemberService;
use app\service\api\notice\NoticeService;
use core\base\BaseApiService;
use core\exception\ApiException;
use core\exception\AuthException;
use core\util\TokenAuth;
use Exception;
use think\facade\Cache;
use think\facade\Log;
use Throwable;
/**
* 登录服务层
* Class BaseService
* @package app\service
*/
class LoginService extends BaseApiService
{
public function __construct()
{
parent::__construct();
$this->model = new Member();
}
/**
* 会员注册
* @param $data
*/
public function register($data)
{
//检测设置是否自动注册
//自动注册检测授权信息
//注册登录
}
/**
* 登录操作
* @param Member $member_info
* @param string $login_type
* @return array
*/
public function login(Member $member_info, string $login_type)
{
//绑定第三方授权
$this->bingOpenid($member_info);
if (!$member_info->status) throw new ApiException('MEMBER_LOCK');
$member_info->login_time = time();
$member_info->login_ip = $this->request->ip();
$member_info->login_channel = $this->channel;
$member_info->login_type = $login_type;
$member_info->login_count++;
$member_info->last_visit_time = time();
$member_info->save();
$token_info = $this->createToken($member_info);
event("MemberLogin", $member_info);
return [
'token' => $token_info['token'],
'expires_time' => $token_info['params']['exp'],
];
}
/**
* 账号登录
* @param string $username
* @param string $password
* @return array|false
*/
public function account(string $username, string $password)
{
$member_service = new MemberService();
$member_info = $member_service->findMemberInfo(['username|mobile' => $username, 'site_id' => $this->site_id]);
if ($member_info->isEmpty()) throw new AuthException('MEMBER_NOT_EXIST');//账号不存在
if (!check_password($password, $member_info->password)) return false;//密码与账号不匹配
return $this->login($member_info, MemberLoginTypeDict::USERNAME);
}
/**
* 手机号登录
* @param string $mobile
* @return array
*/
public function mobile(string $mobile){
//校验手机验证码
$this->checkMobileCode($mobile);
//登录注册配置
$config = (new MemberConfigService())->getLoginConfig();
$is_mobile = $config['is_mobile'];
if($is_mobile != 1) throw new AuthException('MOBILE_LOGIN_UNOPENED');
$member_service = new MemberService();
$member_info = $member_service->findMemberInfo(['mobile' => $mobile, 'site_id' => $this->site_id]);
if ($member_info->isEmpty()) throw new AuthException('MEMBER_NOT_EXIST');//账号不存在
return $this->login($member_info, MemberLoginTypeDict::MOBILE);
}
/**
* 生成token
* @param $member_info
* @return array|null
*/
public function createToken($member_info): ?array
{
$expire_time = env('system.api_token_expire_time') ?? 3600;//todo 不一定和管理端合用这个token时限
return TokenAuth::createToken($member_info->member_id, AppTypeDict::API, ['member_id' => $member_info->member_id, 'username' => $member_info->username, 'site_id' => $member_info->site_id], $expire_time);
}
/**
* 登陆退出(当前账户)
*/
public function logout(): ?bool
{
self::clearToken($this->member_id, $this->request->apiToken());
return true;
}
/**
* 清理token
* @param int $member_id
* @param string|null $token
* @return bool|null
*/
public static function clearToken(int $member_id, ?string $token = ''): ?bool
{
TokenAuth::clearToken($member_id, AppTypeDict::API, $token);
return true;
}
/**
* 解析token
* @param string|null $token
* @return array
*/
public function parseToken(?string $token){
if(empty($token))
{
//定义专属于授权认证机制的错误响应, 定义专属语言包
throw new AuthException('MUST_LOGIN', 401);
}
try {
$token_info = TokenAuth::parseToken($token, AppTypeDict::API);
} catch ( Throwable $e ) {
// if(env('app_debug', false)){
// throw new AuthException($e->getMessage(), 401);
// }else{
throw new AuthException('LOGIN_EXPIRE', 401);
// }
}
if(!$token_info)
{
throw new AuthException('MUST_LOGIN', 401);
}
//验证有效次数或过期时间
return $token_info;
}
/**
* 手机发送验证码
* @param $mobile
* @param string $type 发送短信的业务场景
* @return array
* @throws Exception
*/
public function sendMobileCode($mobile, string $type = ''){
(new CaptchaService())->check();
if(empty($mobile)) throw new AuthException('MOBILE_NEEDED');
//发送
if(!in_array($type, SmsDict::SCENE_TYPE)) throw new AuthException('MEMBER_MOBILE_CAPTCHA_ERROR');
$code = str_pad(random_int(1, 9999), 4, 0, STR_PAD_LEFT);// 生成4位随机数左侧补0
(new NoticeService())->send('member_verify_code', ['code' => $code, 'mobile' => $mobile]);
//将验证码存入缓存
$key = md5(uniqid(null, true));
$cache_tag_name = "mobile_key".$mobile.$type;
$this->clearMobileCode($mobile, $type);
Cache::tag($cache_tag_name)->set($key, [ 'mobile' => $mobile, 'code' => $code, 'type' => $type], 600);
return ['key' => $key];
}
public function getMobileCodeCacheName(){
}
public function clearMobileCode($mobile, $type){
$cache_tag_name = "mobile_key".$mobile.$type;
Cache::tag($cache_tag_name)->clear();
}
/**
* 校验手机验证码
* @param string $mobile
* @return true
*/
public function checkMobileCode(string $mobile){
if(empty($mobile)) throw new AuthException('MOBILE_NEEDED');
$mobile_key = request()->param('mobile_key', '');
$mobile_code = request()->param('mobile_code', '');
if(empty($mobile_key) || empty($mobile_code)) throw new AuthException('MOBILE_CAPTCHA_ERROR');
$cache = Cache::get($mobile_key);
if(empty($cache)) throw new AuthException('MOBILE_CAPTCHA_ERROR');
$temp_mobile = $cache['mobile'];
$temp_code = $cache['code'];
$temp_type = $cache['type'];
if($temp_mobile != $mobile || $temp_code != $mobile_code) throw new AuthException('MOBILE_CAPTCHA_ERROR');
$this->clearMobileCode($temp_mobile, $temp_type);
return true;
}
/**
* 绑定openid
* @param $member
* @return true
*/
public function bingOpenid($member){
$config = (new MemberConfigService())->getLoginConfig();
$is_auth_register = $config['is_auth_register'];
$open_id = $this->request->param('openid');
if(!empty($open_id)){
Log::write('channel_1'.$this->channel);
if(!empty($this->channel)){
$openid_field = match($this->channel){
'wechat' => 'wx_openid',
'weapp' => 'weapp_openid',
default => ''
};
if(!empty($openid_field)){
if(!$member->isEmpty()){
if(empty($member->$openid_field)){
//todo 定义当前第三方授权方没有退出登录功能,故这儿不做openid是否存在账号验证
// $member_service = new MemberService();
// $open_member = $member_service->findMemberInfo([$openid_field => $open_id, 'site_id' => $this->site_id]);
$member->$openid_field = $open_id;
$member->save();
}else{
if( $member->$openid_field != $open_id){
throw new AuthException('MEMBER_IS_BIND_AUTH');
}
}
}
}
}
}
return true;
}
/**
* 重置密码
* @param string $mobile
* @param string $password
*/
public function resetPassword(string $mobile, string $password){
$member_service = new MemberService();
//校验手机验证码
$this->checkMobileCode($mobile);
$member_info = $member_service->findMemberInfo(['mobile' => $mobile, 'site_id' => $this->site_id]);
if ($member_info->isEmpty()) throw new AuthException('MOBILE_NOT_EXIST_MEMBER');//账号不存在
//todo 需要考虑一下,新的密码和原密码一样能否通过验证
$password_hash = create_password($password);
$data = array(
'password' => $password_hash,
);
$member_service->editByFind($member_info, $data);
self::clearToken($member_info['member_id'], $this->request->apiToken());
return true;
}
public function loginScanCode(){
}
public function loginByScanCode(){
}
public function checkScanCode(){
}
}