如何检查登录用户是否拥有访问其他控制器的权限?
解决方案
要实现这个需求,你可以结合Symfony的注解解析器、权限校验组件和访问控制映射来完成,以下是具体实现步骤:
1. 解析控制器的IsGranted注解
使用Symfony的AnnotationReader读取目标控制器类(或__invoke方法)上的IsGranted注解,注意方法级注解优先级高于类级注解。
use Doctrine\Common\Annotations\AnnotationReader; use Symfony\Component\Security\Http\Annotation\IsGranted; private function getControllerIsGrantedAnnotations(string $controllerClass): array { $reader = new AnnotationReader(); $reflectionClass = new \ReflectionClass($controllerClass); // 读取类级注解 $classAnnotations = $reader->getClassAnnotations($reflectionClass); $isGrantedAnnotations = array_filter($classAnnotations, fn($anno) => $anno instanceof IsGranted); // 读取__invoke方法的注解(优先级更高) if ($reflectionClass->hasMethod('__invoke')) { $methodAnnotations = $reader->getMethodAnnotations($reflectionClass->getMethod('__invoke')); $methodIsGranted = array_filter($methodAnnotations, fn($anno) => $anno instanceof IsGranted); if (!empty($methodIsGranted)) { return $methodIsGranted; } } return $isGrantedAnnotations; }
2. 校验注解中的权限/表达式
拿到IsGranted注解后,用AuthorizationCheckerInterface和ExpressionLanguage判断当前用户是否有权限,同时处理带实体主题的表达式。
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; use Symfony\Component\Security\Core\Security; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; private function checkIsGrantedAnnotation(IsGranted $annotation, $subject, AuthorizationCheckerInterface $authorizationChecker): bool { // 处理简单权限字符串 if (!empty($annotation->attributes)) { foreach ($annotation->attributes as $attribute) { if (!$authorizationChecker->isGranted($attribute, $subject)) { return false; } } return true; } // 处理表达式 if (!empty($annotation->expression)) { $expressionLanguage = new ExpressionLanguage(); $variables = [ 'user' => $authorizationChecker->getUser(), 'subject' => $subject, 'security' => new Security($authorizationChecker), ]; return $expressionLanguage->evaluate($annotation->expression, $variables); } return true; }
3. 结合security.yaml中的访问控制规则
除了注解,还要检查security.yaml里的access_control约束,用AccessMapInterface匹配控制器对应的路由,判断用户是否符合规则。
use Symfony\Component\Security\Http\AccessMapInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\RouterInterface; private function checkAccessControl(string $controllerClass, AccessMapInterface $accessMap, AuthorizationCheckerInterface $authorizationChecker, RouterInterface $router): bool { // 反向解析控制器对应的路由 $targetRoute = null; foreach ($router->getRouteCollection()->all() as $routeName => $routeObj) { if ($routeObj->getDefault('_controller') === $controllerClass) { $targetRoute = $routeObj; break; } } if (!$targetRoute) { return true; // 无对应路由,跳过检查 } // 模拟请求匹配AccessMap规则 $request = Request::create($targetRoute->getPath()); list($accessConfigs) = $accessMap->getPatterns($request); foreach ($accessConfigs as $accessConfig) { if (!$authorizationChecker->isGranted($accessConfig->getRoles(), $request)) { return false; } } return true; }
4. 封装成统一的检查函数
整合以上逻辑,实现你需要的控制器权限批量检查功能:
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; use Symfony\Component\Security\Http\AccessMapInterface; use Symfony\Component\Routing\RouterInterface; public function getAvailableActions(array $controllers, $subject): array { $authorizationChecker = $this->container->get(AuthorizationCheckerInterface::class); $accessMap = $this->container->get(AccessMapInterface::class); $router = $this->container->get(RouterInterface::class); $result = []; foreach ($controllers as $actionName => $controllerClass) { // 检查注解权限 $annotations = $this->getControllerIsGrantedAnnotations($controllerClass); $annotationAllowed = true; foreach ($annotations as $anno) { if (!$this->checkIsGrantedAnnotation($anno, $subject, $authorizationChecker)) { $annotationAllowed = false; break; } } // 检查access_control规则 $accessControlAllowed = $this->checkAccessControl($controllerClass, $accessMap, $authorizationChecker, $router); $result[$actionName] = $annotationAllowed && $accessControlAllowed; } return $result; }
注意事项
- 确保已安装
doctrine/annotations包:composer require doctrine/annotations - 表达式中的特殊变量可根据需求补充到
$variables数组中 - 若控制器对应多个路由,需遍历所有路由完成access_control检查
- 可缓存注解解析结果,避免重复反射类影响性能
内容的提问来源于stack exchange,提问作者D3strukt0r
相关产品推荐
相关产品推荐

