Авторизация с помощью токена для API

от автора

Как и обещал ранее, продолжаю свою серию статей про создание API на Symfony2. Сегодня я бы хотел рассказать о авторизации. Из популярных бандлов есть JWTAuthenticationBundle и FOSOAuthServerBundle, у каждого есть свои плюсы и минусы, но мне хотелось бы рассказать как сделать авторизацию самому, чтобы понимать как это работает.

Для начала, создадим сущность UserAccessToken, в которой будем хранить токены доступа пользователей.

<?php  namespace App\CommonBundle\Entity;  use Doctrine\ORM\Mapping AS ORM;  /**  * @ORM\Entity  * @ORM\Table(name="user_access_tokens")  */ class UserAccessToken {     /**      * @ORM\Id      * @ORM\GeneratedValue(strategy="AUTO")      * @ORM\Column(name="id", type="integer")      */     protected $id;      /**      * @ORM\ManyToOne(targetEntity="User")      * @ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="SET NULL")      */     protected $user;      /**      * Тут мы будем хранить наш токен. Токен необходимо генерировать самому и как можно сложнее и длиннее, чтобы исключить возможность подбора      *       * @ORM\Column(name="access_token", type="string")      */     protected $accessToken;      /**      * Дата, после которой токен будет считаться не активным      *       * @ORM\Column(name="expired_at", type="datetime")      */     protected $expiredAt;      /**      * @ORM\Column(name="created_at", type="datetime")      */     protected $createdAt;  } 

Теперь создадим Listener, который будет слушать все запросы к вашему API и авторизировать пользователя.

<?php  namespace App\CommonBundle\Listener;  use App\CommonBundle as Common; use Doctrine\ORM\EntityManager; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;  class AccessTokenListener {     private $entityManager;     private $securityContext;     private $exclude = [         '/users/login', '/users/registration',     ];      const EMPTY_ACCESS_TOKEN = 'empty_access_token';     const INVALID_ACCESS_TOKEN = 'invalid_access_token';     const ACCESS_TOKEN_EXPIRED = 'access_token_expired';      public function __construct(EntityManager $entityManager, SecurityContextInterface $securityContext)     {         $this->entityManager = $entityManager;         $this->securityContext = $securityContext;     }      /**      * @return Common\Entity\UserAccessToken      */     private function getByAccessToken($accessToken)     {         return $this->entityManager->getRepository('CommonBundle:UserAccessToken')->findOneByAccessToken($accessToken);     }      public function beforeController(GetResponseEvent $event)     {         // Делаем доступными без токена необходимые для нас URL         // Это необходимо для того, чтобы открыть метод для авторизации и тд         if (in_array($event->getRequest()->getPathInfo(), $this->exclude)) {             return;         }          // Смотрим заголовок с названием X-Access-Token, если он пустой – возвращаем ошибку         $accessToken = $event->getRequest()->headers->get('X-Access-Token');         if (!$accessToken) {             $event->setResponse(new JsonResponse(['error' => self::EMPTY_ACCESS_TOKEN], 403));             return;         }          // Ищем в базе пользователя по токену         $token = $this->getByAccessToken($accessToken);         if (!$token) {             $event->setResponse(new JsonResponse(['error' => self::INVALID_ACCESS_TOKEN], 403));             return;         }          // Проверяем актуален ли все еще токен         if ($token->getExpiredAt() <= new \DateTime('now')) {             $event->setResponse(new JsonResponse(['error' => self::ACCESS_TOKEN_EXPIRED], 403));             return;         }          // Устанавливаем пользователя, чтобы он был доступен в контроллерах через $this->getUser()         $user = $token->getUser();          $usernamePasswordToken = new UsernamePasswordToken($user, $user->getPassword(), "main", $user->getRoles());         $this->securityContext->setToken($usernamePasswordToken);     } } 

И подключим его в services.yml

    common.listener.access_token:         class: App\CommonBundle\Listener\AccessTokenListener         arguments: [@doctrine.orm.entity_manager, @security.context]         tags:             - { name: kernel.event_listener, event: kernel.request, method: beforeController } 

На этом практически все, теперь только остается сделать простенький метод для авторизации, который будет принимать логин и пароль и в случае успеха создавать новый UserAccessToken со сгенерированным значением accessToken и возвращать его в ответе.

ссылка на оригинал статьи http://habrahabr.ru/post/258903/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *