Hyperf:自定义注解、切面实现权限校验

2022/9/11 4:23:10

本文主要是介绍Hyperf:自定义注解、切面实现权限校验,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

概要

权限控制

权限控制:无论是基于角色,还是根据实际需求开发的基于部门、岗位、员工的系统访问权限控制,最终都是当用户登录系统时或登录后访问某项功能或菜单时基于设定的规则去查验是否能够正常访问。
实现权限控制的方式有很多种。最常见的是通过一个中间件类拦截请求,获取到请求的Path,然后从提前放入缓存或实时查询的权限规则中查找是否存在这个Path,如果存在则放行,反之则无权限访问。

今天介绍的是一种通过自定义注解、切面实现的权限校验的方法。

原理介绍

项目在启动时会扫描项目文件,把相关文件加载入缓存中,例如配置文件、各种注解:@Inject、@Listener、@Controller等等。

具体实现

自定义注解

创建一个注解类,这个要继承AbstractAnnotation这个抽象类,它实现了一下方法(先不用关心):
图片描述

红色部分:是收集注解的方法。绿色部分是绑定参数用的。

自定义的注解类:
图片描述

  • @Annotation 表示这是一个注解类;
  • @Target 表示注解作用域 ,有如下参数:
    • METHOD 注解允许定义在类方法上
    • PROPERTY 注解允许定义在类属性上
    • CLASS 注解允许定义在类上
    • ALL 注解允许定义在任何地方
注解使用
 /**
 * @Controller(prefix="/index")
 */
class IndexController
{
   /**
     * @GetMapping("show")
     * @PreAuthorization(value="index:show")
     */
    public function show() {
        return "show";
    }


}

当项目启动时@PreAuthorization注解信息就会注入,value值就是权限标识,后续校验时使用。
当访问 http://localhost/index/show 时通过切面去做权限校验。

自定义切面
/**
 * @Aspect
 */
#[Aspect]
class PreAuthorizationAspect extends AbstractAspect
{
    protected ContainerInterface $container;

    protected RequestInterface $request;

    protected Logger $logger;

   // 要切入的注解,具体切入的还是使用了这些注解的类,仅可切入类注解和类方法注解
    public $annotations = [
      PreAuthorization::class
    ];

    public function __construct(ContainerInterface $container, RequestInterface $request, Logger $logger)
    {
        $this->container = $container;
        $this->request = $request;
        $this->logger = $logger;
    }

    public function process(ProceedingJoinPoint $proceedingJoinPoint)
    {
      // 切面切入后,执行对应的方法会由此来负责
        try {
            $authorization = $this->getAuthorizationAnnotation($proceedingJoinPoint->className, $proceedingJoinPoint->methodName);
            if (!$this->checkPermission($authorization->value)) {
                throw new BusinessException(ErrorCode::AUTH_INVALID, ErrorCode::getMessage(ErrorCode::AUTH_INVALID));
            }
        } catch (AnnotationException | NotFoundExceptionInterface | ContainerExceptionInterface $e) {
            $this->logger->info("PreAuthorizationAspect 执行过程异常:%s", [
                'code' => $e->getCode(),
                'message' => $e->getMessage(),
                'file' => $e->getFile(),
                'line' => $e->getLine()
            ]);
        }
        try {
            return $proceedingJoinPoint->process();
        } catch (Exception $e) {
            $this->logger->info("PreAuthorizationAspect 执行过程异常:%s", [
                'code' => $e->getCode(),
                'message' => $e->getMessage(),
                'file' => $e->getFile(),
                'line' => $e->getLine()
            ]);
        }
    }

    /**
     * desc: 获取注解类
     * @param string $className
     * @param string $method
     * @return PreAuthorization
     * @throws AnnotationException
     */
    protected function getAuthorizationAnnotation(string $className, string $method): PreAuthorization {
        $annotation = AnnotationCollector::getClassMethodAnnotation($className, $method)[PreAuthorization::class] ?? null;
        if (!$annotation instanceof PreAuthorization) {
            throw new AnnotationException("Annotation PreAuthorization couldn't be collected successfully.");
        }
        return $annotation;
    }

    /**
     * desc: 校验操作权限
     * @param string $annotationValue
     * @return bool
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    protected function checkPermission(string $annotationValue): bool {
        $token = UserAuth::instance()->getToken();
        $cache = di()->get(Redis::class)->get(CacheConstant::USER_AUTH_KEY.$token);
        $cache = json_decode($cache, true);
        // 判断是不是超级管理员
        if (SysUser::isSuperAdmin($cache["userId"])) {
            return true;
        }
        // 判断注解中的value值是否在其中
        $hasPerms = $cache["perms"];
        if (in_array($annotationValue, $hasPerms)) {
            return true;
        }
        return false;
    }
}


这篇关于Hyperf:自定义注解、切面实现权限校验的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程