我有三个实体:用户,商店和类别。 用户与商店具有双向关系,商店与商品具有双向关系。 每个用户可以创建许多商店,他可以为每个商店创建许多类别。 我已经设法使用选民保护商店,用户只能访问他的商店。
这是商店的路线
dashboard_store_view:
path: /{id}/view
defaults: { _controller: ProjectStoreBundle:StoreDashboard:view }
网址就像这样
http://localhost/project/web/app_dev.php/dashboard/store/1/view
这是控制器 StoreDashboardController.php
<?php
//..................
public function viewAction(Store $store)
{
// keep in mind, this will call all registered security voters
if (false === $this->get('security.context')->isGranted('view', $store)) {
throw new AccessDeniedException('Unauthorised access!');
}
$em = $this->getDoctrine()->getManager();
$store = $em->getRepository('ProjectStoreBundle:Store')->findOneById($store);
return $this->render('ProjectDashboardBundle:Store:view.html.twig',
array(
'store' => $store
));
}
这是 StoreVoter
<?php
namespace Project\StoreBundle\Security\Authorization\Voter;
use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\User\UserInterface;
class StoreVoter implements VoterInterface
{
const VIEW = 'view';
const EDIT = 'edit';
const DELETE = 'delete';
public function supportsAttribute($attribute)
{
return in_array($attribute, array(
self::VIEW,
self::EDIT,
self::DELETE,
));
}
public function supportsClass($class)
{
$supportedClass = 'Project\StoreBundle\Entity\Store';
return $supportedClass === $class || is_subclass_of($class, $supportedClass);
}
/**
* @var \Project\StoreBundle\Entity\Store $store
*/
public function vote(TokenInterface $token, $store, array $attributes)
{
// check if class of this object is supported by this voter
if (!$this->supportsClass(get_class($store))) {
return VoterInterface::ACCESS_ABSTAIN;
}
// check if the voter is used correct, only allow one attribute
// this isn't a requirement, it's just one easy way for you to
// design your voter
if(1 !== count($attributes)) {
throw new InvalidArgumentException(
'Only one attribute is allowed for VIEW or EDIT'
);
}
// set the attribute to check against
$attribute = $attributes[0];
// get current logged in user
$user = $token->getUser();
// check if the given attribute is covered by this voter
if (!$this->supportsAttribute($attribute)) {
return VoterInterface::ACCESS_ABSTAIN;
}
// make sure there is a user object (i.e. that the user is logged in)
if (!$user instanceof UserInterface) {
return VoterInterface::ACCESS_DENIED;
}
switch($attribute) {
case 'view':
// we assume that our data object has a method getUser() to
// get the current owner user entity for this data object
if ($user->getId() === $store->getUser()->getId()) {
return VoterInterface::ACCESS_GRANTED;
}
break;
case 'edit':
// we assume that our data object has a method getUser() to
// get the current owner user entity for this data object
if ($user->getId() === $store->getUser()->getId()) {
return VoterInterface::ACCESS_GRANTED;
}
break;
case 'delete':
// we assume that our data object has a method getUser() to
// get the current owner user entity for this data object
if ($user->getId() === $store->getUser()->getId()) {
return VoterInterface::ACCESS_GRANTED;
}
break;
}
}
}
我尝试对类别做同样的事情,但我没有将每个类别保护到他自己的商店,所以evry用户可以编辑任何类别
这是路线
dashboard_category_edit:
pattern: /{store_id}/edit/{id}
defaults: { _controller: ProjectStoreBundle:CategoryDashboard:edit }
网址就像这样
http://localhost/project/web/app_dev.php/dashboard/categories/store/1/edit/3
CategoryDashboardController.php
public function editAction(Category $category, Store $store)
{
// keep in mind, this will call all registered security voters
if (false === $this->get('security.context')->isGranted('edit', $store)) {
throw new AccessDeniedException('Unauthorised access!');
}
$form = $this->createForm(new CategoryEditType(), $category);
$request = $this->getRequest();
if ($request->getMethod() == 'POST')
{
$form->bind($request);
if ($form->isValid())
{
$em = $this->getDoctrine()->getManager();
$em->persist($category);
$em->flush();
$this->get('session')->getFlashBag()->add('info', 'Category bien modifié');
return $this->redirect( $this->generateUrl('dashboard_category_index', array('store_id' => $store->getId())));
}
}
return $this->render('ProjectDashboardBundle:Category:edit.html.twig',
array(
'form' => $form->createView() ,
'store' => $store
));
}
这是 CategoryVoter
<?php
namespace Project\StoreBundle\Security\Authorization\Voter;
use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\User\UserInterface;
class CategoryVoter implements VoterInterface
{
const VIEW = 'view';
const EDIT = 'edit';
const DELETE = 'delete';
public function supportsAttribute($attribute)
{
return in_array($attribute, array(
self::VIEW,
self::EDIT,
self::DELETE,
));
}
public function supportsClass($class)
{
$supportedClass = 'Project\StoreBundle\Entity\Category';
return $supportedClass === $class || is_subclass_of($class, $supportedClass);
}
/**
* @var \Project\StoreBundle\Entity\Category $category
*/
public function vote(TokenInterface $token, $category, array $attributes)
{
// check if class of this object is supported by this voter
if (!$this->supportsClass(get_class($category))) {
return VoterInterface::ACCESS_ABSTAIN;
}
// check if the voter is used correct, only allow one attribute
// this isn't a requirement, it's just one easy way for you to
// design your voter
if(1 !== count($attributes)) {
throw new InvalidArgumentException(
'Only one attribute is allowed for VIEW or EDIT'
);
}
// set the attribute to check against
$attribute = $attributes[0];
// get current logged in user
$user = $token->getUser();
// check if the given attribute is covered by this voter
if (!$this->supportsAttribute($attribute)) {
return VoterInterface::ACCESS_ABSTAIN;
}
// make sure there is a user object (i.e. that the user is logged in)
if (!$user instanceof UserInterface) {
return VoterInterface::ACCESS_DENIED;
}
switch($attribute) {
case 'view':
// we assume that our data object has a method getUser() to
// get the current owner user entity for this data object
if ($user->getId() === $category->getStore()->getUser()->getId()) {
return VoterInterface::ACCESS_GRANTED;
}
break;
case 'edit':
// we assume that our data object has a method getUser() to
// get the current owner user entity for this data object
if ($user->getId() === $category->getStore()->getUser()->getId()) {
return VoterInterface::ACCESS_GRANTED;
}
break;
case 'delete':
// we assume that our data object has a method getUser() to
// get the current owner user entity for this data object
if ($user->getId() === $category->getStore()->getUser()->getId()) {
return VoterInterface::ACCESS_GRANTED;
}
break;
}
}
}
问题是类别不是针对用户进行的,而是与商店有关,那么我该如何保护呢?
我发现此解决方案正在进行验证,如果$ category-&gt; getStore&lt;&gt; $ store所以抛出 AccessDeniedException 而不使用选民,现在它可以正常工作。
if ($category->getStore() <> $store) {
throw new AccessDeniedException('Unauthorised access!');
}
所以控制器将是这样的
/**
* @ParamConverter("store", options={"mapping": {"store_id":"id"}})
*/
public function editAction(Category $category, Store $store)
{
if ($category->getStore() <> $store) {
throw new AccessDeniedException('Unauthorised access!');
}
$form = $this->createForm(new CategoryEditType(), $category);
$request = $this->getRequest();
if ($request->getMethod() == 'POST')
{
$form->bind($request);
if ($form->isValid())
{
$em = $this->getDoctrine()->getManager();
$em->persist($category);
$em->flush();
$this->get('session')->getFlashBag()->add('info', 'Category bien modifié');
return $this->redirect( $this->generateUrl('dashboard_category_index', array('store_id' => $store->getId())));
}
}
return $this->render('ProjectDashboardBundle:Category:edit.html.twig',
array(
'form' => $form->createView() ,
'store' => $store
));
}
这是一个很好的解决方案吗?
答案 0 :(得分:0)
如果每个Category
只有一个Store
,则在您想要修改store_id
时,在路由中使用Category
毫无意义。只需使用category_id
并通过调用$store
从$category
获取$store = $category->getStore();
即可。更改editAction
:
/**
* @ParamConverter("category", options={"mapping": {"category_id":"id"}})
*/
public function editAction(Category $category)
{
// keep in mind, this will call all registered security voters
if (false === $this->get('security.context')->isGranted('edit', $category)) {
throw new AccessDeniedException('Unauthorised access!');
}
$store = $category->getStore();
(...)
答案 1 :(得分:0)
我发现这个解决方案是在表类别中获取商店的ID,然后进行两次验证, 如果表类别中的id_store与商店所有者不匹配,并且表类别中的id_store与当前商店不匹配
/**
* @ParamConverter("store", options={"mapping": {"store_id":"id"}})
*/
public function editAction(Category $category, Store $store)
{
// get id_store in table category
$idStore = $category->getStore();
// if id_store in table category doesn't match user
if (false === $this->get('security.context')->isGranted('edit', $idStore)) {
throw new AccessDeniedException('Unauthorised access!');
}
// if id_store in table category doesn't match current store
if (false === ($idStore === $store)) {
throw new AccessDeniedException('Unauthorised access!');
}
$form = $this->createForm(new CategoryEditType(), $category);
$request = $this->getRequest();
if ($request->getMethod() == 'POST')
{
$form->bind($request);
if ($form->isValid())
{
$em = $this->getDoctrine()->getManager();
$em->persist($category);
$em->flush();
$this->get('session')->getFlashBag()->add('info', 'Category bien modifié');
return $this->redirect( $this->generateUrl('dashboard_category_index', array('store_id' => $store->getId())));
}
}
return $this->render('ProjectDashboardBundle:Category:edit.html.twig',
array(
'form' => $form->createView() ,
'store' => $store
));
}