Symfony无法识别我的Voter类。建议我实现已经实现的VoterInterface。
我都实现了VoterInterface并扩展了Voter类
services.yml
parameters:
admin_mail: manager@example.com
services:
# default configuration for services in *this* file
_defaults:
# automatically injects dependencies in your services
autowire: true
# automatically registers your services as commands, event subscribers, etc.
autoconfigure: true
# this means you cannot fetch services directly from the container via $container->get()
# if you need to do this, you can override this setting on individual services
public: false
bind:
$adminMail: 'manager@example.com'
$requestLogger: '@monolog.logger.request'
Psr\Log\LoggerInterface: '@monolog.logger.request'
AppBundle\:
resource: '../../src/AppBundle/*'
# you can exclude directories or files
# but if a service is unused, it's removed anyway
exclude: '../../src/AppBundle/{Entity,Repository,Tests}'
# controllers are imported separately to make sure they're public
# and have a tag that allows actions to type-hint services
AppBundle\Controller\:
resource: '../../src/AppBundle/Controller'
public: true
tags: ['controller.service_arguments']
app.form_login_authenticator:
class: AppBundle\Security\FormLoginAuthenticator
arguments: ["@router", "@security.password_encoder"]
app.post_entity:
class: AppBundle\Entity\Post
arguments: ['@security.helper']
app.post_voter:
class: AppBundle\Security\PostVoter
arguments: ['@security.access.decision_manager']
tags:
- { name: security.voter }
security.yml
security:
encoders:
AppBundle\Entity\User:
algorithm: bcrypt
# https://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded
providers:
in_memory:
memory:
users:
ryan:
password: ryanpass
roles: 'ROLE_USER'
admin:
password: kitten
roles: 'ROLE_ADMIN'
database_users:
entity:
class: AppBundle:User
property: username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: ~
form_login:
login_path: login
check_path: login
username_parameter: _username
logout:
path: /logout
target: /login
invalidate_session: true
provider: database_users
logout_on_user_change: true
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: ROLE_USER }
access_decision_manager:
strategy: unanimous
allow_if_all_abstain: false
PostVoter.php
<?php
namespace AppBundle\Security;
use AppBundle\Entity\Post;
use AppBundle\Entity\User;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\User\UserInterface;
class PostVoter extends Voter implements VoterInterface
{
const VIEW = 'view';
const EDIT = 'edit';
const DELETE = 'delete';
private $decisionManager;
public function __construct(AccessDecisionManagerInterface $decisionManager)
{
$this->decisionManager = $decisionManager;
}
protected function supports($attribute, $subject)
{
if (!in_array($attribute, [self::VIEW, self::EDIT, self::DELETE])) {
return true;
}
if (!$subject instanceof Post) {
return false;
}
return true;
}
protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
{
$user = $token->getUser();
if (!$user instanceof User) {
return false;
}
$post = $subject;
switch($attribute) {
case self::VIEW:
return $this->canView($post, $user);
case self::EDIT:
return $this->canEdit($post, $user);
case self::DELETE:
return $this->canDelete($post, $user);
}
throw new \LogicException('This code should not be reached!');
}
private function canView(Post $post, User $user)
{
if ($this->canEdit($post, $user)) {
return true;
}
return !$post->isPrivate();
}
private function canEdit(Post $post, User $user)
{
return $user->isEqualTo($post->getUser());
}
private function canDelete(Post $post, User $user)
{
return canEdit($user, $post);
}
public function vote(TokenInterface $token, $subject, $attributes)
{
foreach ($attributes as $attribute) {
if (!$this->supports($attribute, $subject) && $this->voteOnAttribute($attribute, $subject)) {
return VoterInterface::ACCESS_DENIED;
}
}
return VoterInterface::ACCESS_GRANTED;
}
}
PostController.php
<?php
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
use AppBundle\Entity\Post;
use AppBundle\Entity\Comment;
use AppBundle\Entity\User;
use AppBundle\Form\PostType;
use AppBundle\Updates\SiteUpdateManager;
use AppBundle\Form\CommentType;
class PostController extends Controller
{
public function indexAction(SiteUpdateManager $siteUpdateManager)
{
$repository = $this->getDoctrine()->getManager()->getRepository('AppBundle:Post');
$posts = $repository->findAllPostWithCommentOrderedByIdDesc();
$post = new Post();
$form = $this->createForm(PostType::class, $post);
return $this->render('post/index.html.twig', [
'posts' => $posts,
'form' => $form->createView()
]);
}
public function showAction(Post $post)
{
$this->denyAccessUnlessGranted('view', $post);
}
public function updateAction()
{
}
public function newAction(Request $request)
{
$post = new Post();
$form = $this->createForm(PostType::class, $post);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid())
{
$image = $post->getImage();
$imageName = $this->generateUniqueFileName(). '.' . $image->guessExtension();
try {
$image->move(
$this->getParameter('post_image_directory'),
$imageName
);
} catch (FileException $e) {
return new Response('File upload error: '.$e->getMessage(), Response::HTTP_INTERNAL_SERVER_ERROR);
}
$post->setUser($this->get('security.token_storage')->getToken()->getUser());
$post->setImage($imageName);
$em = $this->getDoctrine()->getManager();
$em->persist($post);
$em->flush();
return $this->redirectToRoute('post_list');
}
return $this->render('post/new.html.twig', [
'form' => $form->createView()
]);
}
public function deleteAction(Post $post)
{
$this->denyAccessUnlessGranted('delete', $post);
$em = $this->getDoctrine()->getManager();
$em->remove($post);
$em->flush();
return $this->redirectToRoute('post_list');
}
public function generateUniqueFileName()
{
return sha1(uniqid(mt_rand(), true));
}
}
Post.php
<?php
namespace AppBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use AppBundle\Entity\User;
use AppBundle\Entity\Comment;
use Symfony\Component\Security\Core\Security;
class Post
{
private $id;
private $title;
private $description;
private $image;
private $comments;
private $user;
private $security;
public function __construct(Security $security)
{
$this->comments = new ArrayCollection();
$this->security = $security;
}
public function getId()
{
return $this->id;
}
public function getTitle()
{
return $this->title;
}
public function setTitle($title)
{
$this->title = $title;
}
public function getDescription()
{
return $this->description;
}
public function setDescription($description)
{
$this->description = $description;
}
public function getImage()
{
return $this->image;
}
public function setImage($image)
{
$this->image = $image;
}
public function getUser()
{
return $this->user;
}
public function setUser(User $user)
{
$this->user = $user;
}
public function getComments()
{
return $this->comments;
}
public function addComment(Comment $comment)
{
$this->comments[] = $comment;
}
public function isPrivate()
{
return in_array("ROLE_USER", $this->security->getUser()->getRoles());
return $this->getUser()->isEqualTo($this->security->getUser());
}
}
User.php
<?php
namespace AppBundle\Entity;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\Common\Collections\ArrayCollection;
use AppBundle\Entity\Post;
use AppBundle\Entity\Comment;
use Symfony\Component\Security\Core\User\EquatableInterface;
class User implements UserInterface, EquatableInterface
{
private $id;
private $email;
private $username;
private $roles;
private $plainPassword;
private $password;
private $posts;
private $comments;
public function __construct()
{
$this->roles = ['ROLE_USER'];
$this->posts = new ArrayCollection();
$this->comments = new ArrayCollection();
}
public function eraseCredentials()
{
}
public function getRoles()
{
return $this->roles;
}
public function getId()
{
return $this->id;
}
public function setUsername($username)
{
return $this->username = $username;
}
public function getUsername()
{
return $this->username;
}
public function getEmail()
{
return $this->email;
}
public function setEmail($email)
{
$this->email = $email;
}
public function getPassword()
{
return $this->password;
}
public function setPassword($password)
{
$this->password = $password;
}
public function getPlainPassword()
{
return $this->plainPassword;
}
public function setPlainPassword($plainPassword)
{
$this->plainPassword = $plainPassword;
}
public function getSalt()
{
return null;
}
public function getComments()
{
return $this->comments;
}
public function addComment(Comment $comment)
{
$this->comments[] = $comment;
}
public function getPosts()
{
return $this->posts;
}
public function addPost(Post $post)
{
$this->posts[] = $post;
}
public function isEqualTo(UserInterface $user) {
if (!$user instanceof User) {
return false;
}
if ($this->getUsername() !== $user->getUsername()) {
return false;
}
if ($this->getPassword() !== $user->getPassword()) {
return false;
}
return true;
}
}
AppBundle \ Security \ PostVoter应该实现Symfony \ Component \ Securi
ty \ Core \ Authorization \ Voter \ VoterInterface接口用作选民。