具有多角色要求的 Symfony2 访问控制
Symfony2 access control with multiple role requirement
在我的站点上,除了某些特定页面(登录、注册、密码重置),用户需要登录。我已经实现了 remember me
功能并且效果很好。
我想实现的是,对于管理页面,用户应该有 admin role and not remembered
。为了检查这个要求,我在相关的 access_control 规则中使用了 allow_if
,但是它拒绝了我的管理员用户的访问,虽然会话没有被记住并且我可以确认会话在调试工具栏上有 UsernamePasswordToken
。
我的access_control规则如下:(第四条不行)
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/user/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/user/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin, allow_if: "has_role('ROLE_ADMIN') and has_role('IS_AUTHENTICATED_FULLY')" }
- { path: ^/, role: IS_AUTHENTICATED_REMEMBERED }
如果我从相关访问控制规则中删除and has_role('IS_AUTHENTICATED_FULLY')
部分,用户可以通过授权所以问题似乎是这部分。
has_role('IS_AUTHENTICATED_FULLY')
有什么问题?
Symfony Version: 2.7.5
Add Code to Deny Access There are two ways to deny access to
something:
- access_control in security.yml allows you to protect URL patterns (e.g. /admin/*). This is easy, but less flexible;
- in your code via the security.authorization_checker service.
您正在尝试使用第一种方式,但只能通过第二种方式制作。
您需要通过在控制器中放入代码来执行授权检查
if (!$this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
throw $this->createAccessDeniedException();
}
您也可以使用 SensioFrameworkExtraBundle 并向您的控制器添加注释
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
...
/**
* @Security("is_granted('IS_AUTHENTICATED_FULLY')")
*/
public function showAction()
{
}
嗯,我找到了解决问题的方法。
How to use Expressions in Security, Routing, Services, and Validation 官方网站上的食谱文章有一个相关部分并说明了可能使用 is_remember_me()
和 is_fully_authenticated()
方法来检查是否存在 IS_AUTHENTICATED_REMEMBERED
和 IS_AUTHENTICATED_FULLY
各自的角色。
is_remember_me is different than checking IS_AUTHENTICATED_REMEMBERED
The is_remember_me and is_authenticated_fully functions are similar to using IS_AUTHENTICATED_REMEMBERED and IS_AUTHENTICATED_FULLY with the isGranted function - but they are not the same. The following shows the difference:
use Symfony\Component\ExpressionLanguage\Expression;
// ...
$ac = $this->get('security.authorization_checker');
$access1 = $ac->isGranted('IS_AUTHENTICATED_REMEMBERED');
$access2 = $ac->isGranted(new Expression(
'is_remember_me() or is_fully_authenticated()'
));
Here, $access1 and $access2 will be the same value. Unlike the behavior of IS_AUTHENTICATED_REMEMBERED and IS_AUTHENTICATED_FULLY, the is_remember_me function only returns true if the user is authenticated via a remember-me cookie and is_fully_authenticated only returns true if the user has actually logged in during this session (i.e. is full-fledged).
使用该文档部分,我修改了我的访问控制规则如下,现在可以使用了:
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/user/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/user/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin, allow_if: "has_role('ROLE_ADMIN') and is_fully_authenticated()" }
- { path: ^/, role: IS_AUTHENTICATED_REMEMBERED }
但是,我仍然认为这是一个问题,而不是预期的行为。所以,我在官方问题跟踪器中打开了一个问题:
Github 问题: #16096 - Expression engine has_role() function can't process implicit roles
在我的站点上,除了某些特定页面(登录、注册、密码重置),用户需要登录。我已经实现了 remember me
功能并且效果很好。
我想实现的是,对于管理页面,用户应该有 admin role and not remembered
。为了检查这个要求,我在相关的 access_control 规则中使用了 allow_if
,但是它拒绝了我的管理员用户的访问,虽然会话没有被记住并且我可以确认会话在调试工具栏上有 UsernamePasswordToken
。
我的access_control规则如下:(第四条不行)
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/user/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/user/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin, allow_if: "has_role('ROLE_ADMIN') and has_role('IS_AUTHENTICATED_FULLY')" }
- { path: ^/, role: IS_AUTHENTICATED_REMEMBERED }
如果我从相关访问控制规则中删除and has_role('IS_AUTHENTICATED_FULLY')
部分,用户可以通过授权所以问题似乎是这部分。
has_role('IS_AUTHENTICATED_FULLY')
有什么问题?
Symfony Version: 2.7.5
Add Code to Deny Access There are two ways to deny access to something:
- access_control in security.yml allows you to protect URL patterns (e.g. /admin/*). This is easy, but less flexible;
- in your code via the security.authorization_checker service.
您正在尝试使用第一种方式,但只能通过第二种方式制作。
您需要通过在控制器中放入代码来执行授权检查
if (!$this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
throw $this->createAccessDeniedException();
}
您也可以使用 SensioFrameworkExtraBundle 并向您的控制器添加注释
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
...
/**
* @Security("is_granted('IS_AUTHENTICATED_FULLY')")
*/
public function showAction()
{
}
嗯,我找到了解决问题的方法。
How to use Expressions in Security, Routing, Services, and Validation 官方网站上的食谱文章有一个相关部分并说明了可能使用 is_remember_me()
和 is_fully_authenticated()
方法来检查是否存在 IS_AUTHENTICATED_REMEMBERED
和 IS_AUTHENTICATED_FULLY
各自的角色。
is_remember_me is different than checking IS_AUTHENTICATED_REMEMBERED
The is_remember_me and is_authenticated_fully functions are similar to using IS_AUTHENTICATED_REMEMBERED and IS_AUTHENTICATED_FULLY with the isGranted function - but they are not the same. The following shows the difference:
use Symfony\Component\ExpressionLanguage\Expression;
// ...
$ac = $this->get('security.authorization_checker');
$access1 = $ac->isGranted('IS_AUTHENTICATED_REMEMBERED');
$access2 = $ac->isGranted(new Expression(
'is_remember_me() or is_fully_authenticated()'
));
Here, $access1 and $access2 will be the same value. Unlike the behavior of IS_AUTHENTICATED_REMEMBERED and IS_AUTHENTICATED_FULLY, the is_remember_me function only returns true if the user is authenticated via a remember-me cookie and is_fully_authenticated only returns true if the user has actually logged in during this session (i.e. is full-fledged).
使用该文档部分,我修改了我的访问控制规则如下,现在可以使用了:
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/user/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/user/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin, allow_if: "has_role('ROLE_ADMIN') and is_fully_authenticated()" }
- { path: ^/, role: IS_AUTHENTICATED_REMEMBERED }
但是,我仍然认为这是一个问题,而不是预期的行为。所以,我在官方问题跟踪器中打开了一个问题:
Github 问题: #16096 - Expression engine has_role() function can't process implicit roles