服务端开发模式-thinkphp-重新整理workman

一、登录接口

复制代码
<?php
/**
 * 登录退出操作
 * User: 龙哥·三年风水
 * Date: 2024/10/29
 * Time: 15:53
 */
namespace app\controller\common;
use app\controller\Emptys;
use app\model\permission\Admin;
use app\model\param\System as SystemModel;
use Email\EmailSender;
use Other\Browser;
use Redis\Redis;
use app\model\common\Token as TokenModel;
use Encipher\Encrypt;
use Workman\GatewayWork;

class Login extends Emptys
{
    //验证码展示
    public function getCode(){
        $re = app()->make(\Other\Captcha::class)->create();
        $base64 = 'data:image/png;base64,' . base64_encode($re->getData());
        return succ('SUCCESS',$base64);
    }

    //登录
    public function doLogin(){
        $param = $this->request->param();
        $validate = new \app\validate\common\Login;
        $result = $validate->check($param);//参数验证
        if (!$result) return err($validate->getError());
        //验证码的正确性
        if (!app()->make(\Other\Captcha::class)->check($param['captcha_code'])) return err('验证码错误');
        //if (time() + 5 < strtotime($param['login_time'])) return err('非法登录');
        $resData = Admin::dataFind(['email' => trim($param['username'])], 'id,realname,password,ip,status', true);
        //用户信息的正确性
        if (empty($resData) && empty($resData['id'])) return err('用户不存在');
        if ($resData['status'] !== 1) return err('该账号已被禁用');
        $systemParam = SystemModel::dataFind(['id' => 1],'security_password,platform_token_expira');
        if ($resData['password'] !== sha1($param['password'] . $systemParam['security_password'])) return err('账号对应的密码错误');
        $loginIp = $this->request->header('x-real-ip');
        if(!empty($resData['ip'])){
            if($resData['ip'] != $loginIp)return err('禁止访问,不在IP白名单中');
        }
        // 获取浏览器版本
        $browserId = Browser::getVersion();
        // 获取该用户是否在登录状态下
        $tokenId = Redis::select(config('cache.stores.redis.token_db'))->get('token_' . $resData['id']);
        // 如果在登录状态下,需要确认IP是否一致
        if(!empty($tokenId)){
            $tokenValue = explode('|', Encrypt::decryptRsa($tokenId));//解密 --转换token
            if(count($tokenValue) > 0){
                $tokenSelect = count($tokenValue) == 2 ? sha1(sha1($tokenValue[0]).strtotime($tokenValue[1])) : $tokenValue[0];
                // 读取用户之前登录的IP及浏览器
                $resTokenLogin = TokenModel::dataFind(['token' => $tokenSelect,'token_type' => 1],'id,random_number,browser_id,login_ip',true);
                // 如果之前登录的IP存在 之前登录的IP与现在登录的IP进行对比
                if(!empty($resTokenLogin)){
                    // 如果不相等的情况下,需要告知之前的浏览器做退出动作
                    if($loginIp != $resTokenLogin['login_ip']){
                        TokenModel::save(['expire_time' => time()], ['token' => $tokenSelect]);
                        $this->setToken(3,'PermissionMemberLogout',$resData['id'],$resTokenLogin['random_number'],$resTokenLogin['browser_id'],$resTokenLogin['login_ip'],$tokenSelect);
                        Redis::select(config('cache.stores.redis.token_db'))->del('token_' . $tokenSelect);
                        GatewayWork::onRepeatClose($resData['id']);
                    }else{
                        // 如果相等的情况下,就需要判断浏览器是否相等啦,如果不相等的情况下,也是需要让之前的账号多退出的动作
                        if($browserId == $resTokenLogin['browser_id'])return err('已登录,不可再次登录');
                        if($browserId != $resTokenLogin['browser_id']){
                            TokenModel::save(['expire_time' => time()], ['token' => $tokenSelect]);
                            $this->setToken(3,'PermissionMemberLogout',$resData['id'],$resTokenLogin['random_number'],$resTokenLogin['browser_id'],$resTokenLogin['login_ip'],$tokenSelect);
                            Redis::select(config('cache.stores.redis.token_db'))->del('token_' . $tokenSelect);
                            GatewayWork::onRepeatClose($resData['id']);
                        }
                    }
                }
            }
        }
        //写入Token日志
        $dataRandomNumber = alnum();
        $expireTime = strtotime($param['login_time']) + $systemParam['platform_token_expira'];
        $token = $resData['id'].$dataRandomNumber;
        $dataToken = sha1( sha1($token). strtotime($param['login_time']));
        $this->setToken(1,'CommonLoginDoLogin',$resData['id'],$dataRandomNumber,$browserId,$loginIp,$dataToken,$expireTime);
        //加入跨站攻击验证队列
        Redis::select(config('cache.stores.redis.token_db'))->setex('token_' . $dataToken,$systemParam['platform_token_expira'],$resData['id']);
        Redis::select(config('cache.stores.redis.token_db'))->setex('token_' . $resData['id'],$systemParam['platform_token_expira'],Encrypt::encryptRsa($dataToken));
        /*$emailSender = new EmailSender();
        $emailSender::send($param['username'],'登录系统',$resData['realname'].'于'.$param['login_time'].'登录系统');*/
        return succ('登录成功',Encrypt::encryptRsa($token));
    }


    /**
     * 操作日志记录
     * User: 龙哥·三年风水
     * Date: 2024/12/12
     * Time: 17:11
     * @ param $tokenType 操作类型
     * @ param $menuName 权限名称
     */

    private function setToken($tokenType,$menuName,$adminId,$randomNumber,$browserId,$loginIp,$token,$expireTime = ''){
        $data['token_type'] = $tokenType;
        $data['menu_name'] = $menuName;
        $data['admin_id'] = $adminId;
        $data['random_number'] = $randomNumber;
        $data['browser_id'] = $browserId;
        $data['login_ip'] = $loginIp;
        $data['create_time'] = date('Y-m-d',time());
        $data['login_time'] = date('Y-m-d H:i:s',time());
        $data['expire_time'] = empty($expireTime) ? time() : $expireTime;
        $data['token'] = $token;
        TokenModel::save($data,[]);
    }
}

二、长链接封装

复制代码
<?php
/**
 * 长链接发送信息
 * User: 龙哥 三年风水
 * Date: 2024/12/21/0021
 * Time: 22:52
 */
namespace Workman;
use GatewayClient\Gateway;
class GatewayWork
{
    //向客户端发送内容
    public static function sendToClient($client_id, $data){
        //获取配置文件
        $isFormal = config('socket.is_formal');
        Gateway::$registerAddress = config("socket.tcp_address.{$isFormal}.address");
        Gateway::sendToClient($client_id, $data);
    }

    //向群推送数据
    public static function sendToGroup($group,$message){
        //获取配置文件
        $isFormal = config('socket.is_formal');
        Gateway::$registerAddress = config("socket.tcp_address.{$isFormal}.address");
        Gateway::sendToGroup($group,json_encode($message));
    }

    //批量向客户端发送内容
    public static function sendToAll($message, $client_id_array = null){
        //获取配置文件
        $isFormal = config('socket.is_formal');
        Gateway::$registerAddress = config("socket.tcp_address.{$isFormal}.address");
        Gateway::sendToAll(json_encode($message), $client_id_array);
    }

    //获取与 uid 绑定的 client_id 列表
    public static function getClientIdByUid($uid){
        //获取配置文件
        $isFormal = config('socket.is_formal');
        Gateway::$registerAddress = config("socket.tcp_address.{$isFormal}.address");
        return Gateway::getClientIdByUid($uid);
    }

    //重复登录关闭客户端
    public static function onRepeatClose($userId){
        //获取配置文件
        $isFormal = config('socket.is_formal');
        Gateway::$registerAddress = config("socket.tcp_address.{$isFormal}.address");
        $resUser = Gateway::isUidOnline((int)$userId);
        if($resUser == 1){
            $resClientUser = Gateway::getClientIdByUid((int)$userId);
            foreach ($resClientUser as $v){
                Gateway::sendToClient($v, json_encode(['type' => 'repeat_close','data' => 'ok']));
                Gateway::destoryClient($v);
            }
        }
    }

    //正常退出关闭客户端
    public static function onArtificialClose($userId){
        //获取配置文件
        $isFormal = config('socket.is_formal');
        Gateway::$registerAddress = config("socket.tcp_address.{$isFormal}.address");
        $resUser = Gateway::isUidOnline((int)$userId);
        if($resUser == 1){
            $resClientUser = Gateway::getClientIdByUid((int)$userId);
            foreach ($resClientUser as $v){
                Gateway::sendToClient($v, json_encode(['type' => 'artificial_close','data' => 'ok']));
                Gateway::destoryClient($v);
            }
        }
    }

    //到期退出关闭客户端
    public static function onExpireClose($userId){
        //获取配置文件
        $isFormal = config('socket.is_formal');
        Gateway::$registerAddress = config("socket.tcp_address.{$isFormal}.address");
        $resUser = Gateway::isUidOnline((int)$userId);
        if($resUser == 1){
            $resClientUser = Gateway::getClientIdByUid((int)$userId);
            foreach ($resClientUser as $v){
                Gateway::sendToClient($v, json_encode(['type' => 'expire_close','data' => 'ok']));
                Gateway::destoryClient($v);
            }
        }
    }
}

三、提前说明

因最近一段时间比较忙,导致精力不能集中。之前封装的vue-element-admin与thinkphp6追加长链接workman项目开发文档中有些问题。所以今天与明后天彻底解决一下这个问题。请谅解。

相关推荐
BingoGo9 小时前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack9 小时前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端
BingoGo1 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php
JaguarJack1 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php·服务端
JaguarJack2 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
后端·php·服务端
BingoGo2 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
php
初次攀爬者3 天前
ZooKeeper 实现分布式锁的两种方式
分布式·后端·zookeeper
JaguarJack3 天前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·php·laravel
郑州光合科技余经理4 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
QQ5110082854 天前
python+springboot+django/flask的校园资料分享系统
spring boot·python·django·flask·node.js·php