hyperf AOP 使用

使用到的知识:

正题

功能是围绕小程序登录做的分为学生端和教师端此处代码为学生端登录逻辑

定义切面(Aspect)

使用切面完成了小程序授权获取信息的功能并使用使用协程上下文将获取的参数传递给控制器

<?php

declare(strict_types=1);

namespace App\Aspect;

use App\Annotation\Login;
use App\Log; 
use EasyWeChat\Factory;
use Hyperf\Di\Annotation\Aspect;
use Hyperf\Di\Aop\AbstractAspect;
use Hyperf\Di\Aop\ProceedingJoinPoint;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Contract\ResponseInterface;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\Redis\Redis;
use Hyperf\Utils\Context;

/**
 * @Aspect
 */
class LoginAspect extends AbstractAspect
{
    /**
     * @Inject
     * @var ResponseInterface
     */
    public $response;

    /**
     * @Inject
     * @var RequestInterface
     */
    public $request;

    /**
     * @Inject
     * @var Log
     */
    protected $logger;

    /**
     * 切入的类
     *
     * @var array
     */
    public $classes = [
        'App\Controller\Api\Student\LoginController::login',
    ];

    /**
     * 切入的注解类
     *
     * @var array
     */
    public $annotations = [
        Login::class
    ];

    /**
     * @param ProceedingJoinPoint $proceedingJoinPoint
     * @return mixed|\Psr\Http\Message\ResponseInterface
     * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
     */
    public function process(ProceedingJoinPoint $proceedingJoinPoint)
    {
        // 日志
        $logger = $this->logger->get('default');

        /** @var Login $auth */
        $auth = $proceedingJoinPoint->getAnnotationMetadata()->method[Login::class];

        if (empty($auth)) {
            $logger->info($proceedingJoinPoint->className . 'Login注解缺失');
            return $this->response->json(['message' => '登录失败', 'code' => '403']);
        }

        $config = config('wechat.' . \App\Models\Student::class);

        if (empty($config)) {
            $logger->info($proceedingJoinPoint->className . '小程序配置缺失');
            return $this->response->json(['message' => '登录失败', 'code' => '403']);
        }

        // 实例化小程序
        $app = Factory::miniProgram($config);

        // 生成协程客户端
        $client = make(ClientFactory::class)->create(data_get($app->getConfig(), 'http'));

        // 更换client
        $app->rebind('http_client', $client);

        // 获取微信CODE
        $code = $this->request->input('code');

        // 获取用户微信信息
        $session = $app->auth->session((string)$code);

        if (empty($session) || isset($session['errcode'])) {
            $logger->info($proceedingJoinPoint->className . '小程序SESSION获取失败');
            return $this->response->json(['message' => '登录失败', 'code' => '403']);
        }

        $unionid = data_get($session, 'unionid');

         //实例化Redis
        $redis = redis();

         //获取用户信息
        $user = $redis->get($auth->auth . ':' . $unionid);

        if (empty($user)) {
             //数据库查询
            $user = make($auth->auth)->where(compact('unionid'))->first();

            if (!empty($user)) {
                $redis->set($auth->auth . ':' . $unionid, serialize($user));
            }
        } else {
            $user = unserialize($user);
        }

        // 此处使用协程上下文传递参数
        Context::set('auth_user', $user);
        Context::set('unionid', $unionid);
        Context::set('openid', data_get($session, 'openid'));

        return $proceedingJoinPoint->process();
    }
}

定义一个注解(annotation)

切面中使用到了注册的参数用于区分不同端用户登录

<?php

namespace App\Annotation;

use Hyperf\Di\Annotation\AbstractAnnotation;

/**
 * @Annotation
 * @Target("METHOD")
 */
class Login extends AbstractAnnotation
{
    /**
     * @var string
     */
    public $auth;
}

控制器逻辑

将获取的用户信息存入数据库

<?php

declare(strict_types=1);

namespace App\Controller\Api\Student;

use App\Controller\Auth\JWT;
use App\Event\LastLoginTimeEvent;
use App\Models\Student;
use EasyWeChat\Factory;
use Hyperf\Di\Annotation\AnnotationCollector;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\HttpServer\Contract\ResponseInterface;
use Hyperf\HttpServer\Annotation\PostMapping;
use App\Annotation\Login;
use Hyperf\Utils\Context;
use App\Controller\Controller as BaseController;
use Hyperf\HttpServer\Annotation\Controller;
use Psr\EventDispatcher\EventDispatcherInterface;

/**
 * @Controller(prefix="/api/student")
 */
class LoginController extends BaseController
{

    /**
     * @Inject
     * @var JWT
     */
    protected $jwt;

    /**
     * @Inject
     * @var EventDispatcherInterface
     */
    private $eventDispatcher;

    /**
     * 登录
     *
     * @PostMapping(path="login")
     * @Login(auth="App\Models\Student")
     */
    public function login(RequestInterface $request, ResponseInterface $response)
    {
        $user = Context::get('auth_user');

        if (empty($user)) {
            $user = Student::create([
                'openid' => Context::get('openid'),
                'unionid' => Context::get('unionid'),
                'avatar' => $request->input('userInfo.avatarUrl'),
                'nickname' => $request->input('userInfo.nickName'),
                'gender' => $request->input('userInfo.gender'),
            ]);
        }

        // 更新最后登录时间
        $this->eventDispatcher->dispatch(new LastLoginTimeEvent($user));

       // 获取 token
        $token = $this->jwt->fromUser($user);

        // 返回数据
        return $this->response(compact('token'));
    }
}
本帖已被设为精华帖!
本帖由系统于 3年前 自动加精
讨论数量: 3

swoole最振奋人心的就是协程了

4年前 评论
了然、 (楼主) 4年前

请问在LoginController 中$request->input('userInfo.avatarUrl'),可以获取到用户信息么,请求用户信息不是在切面请求到么

4年前 评论
了然、 (楼主) 4年前
search-in (作者) 4年前
了然、 (楼主) 4年前
search-in (作者) 4年前
wenber

jwt是用的三方库吗?

4年前 评论
了然、 (楼主) 4年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!