概要
权限控制
权限控制:无论是基于角色,还是根据实际需求开发的基于部门、岗位、员工的系统访问权限控制,最终都是当用户登录系统时或登录后访问某项功能或菜单时基于设定的规则去查验是否能够正常访问。
实现权限控制的方式有很多种。最常见的是通过一个中间件类拦截请求,获取到请求的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;
}
}
点击查看更多内容
1人点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦