Простая аутентификация ключа API в Symfony2 с использованием FOSUserBundle (и HWIOauthBundle), заполнение пробелов
Изменить: см. ниже мое собственное решение, которое на момент написания статьи функционирует, но несовершенно. Хотелось бы услышать критику и отзывы, если я получу что-то, что я чувствую, действительно твердо, тогда я сделаю запись в блоге для других людей, сталкивающихся с тем же вызовом.
Я боролся с этим в течение нескольких дней, и я надеюсь, что кто-то может сообщить мне, если я на правильном пути.
У меня есть система с веб-сервисом FOSRestBundle, в котором я в настоящее время использую FOSUserBundle и HWIOAuthBundle для аутентификации пользователей.
Я хотел бы настроить аутентификацию аутентификации apache для системы webservice.
Я прочитал http://symfony.com/doc/current/cookbook/security/api_key_authentication.html, и это кажется достаточно простым для реализации, я также установил UecodeApiKeyBundle, который, кажется, в основном просто реализация этой страницы книги.
Мой вопрос - это n00b... что теперь? На странице книги и в наборе оба документа проверяют подлинность пользователя по ключу API, но не затрагивают поток пользователей регистрации, генерируя ключи API, позволяя пользователям регистрироваться и т.д. Мне бы очень понравились простые конечные точки API для входа, регистрации и выхода из системы, которые могут использовать мои разработчики приложений. Что-то вроде /api/v 1/login и т.д.
Я думаю, что могу справиться с регистрацией.... логин меня сбивает с толку. Основываясь на некотором дополнительном чтении, мне кажется, что мне нужно сделать для входа в систему:
-
Создайте контроллер на api/v1/login, который принимает запросы POST.
запрос будет выглядеть как {_username: foo, _password: bar} или
что-то вроде {facebook_access_token: foo. Кроме того, для входа в facebook может потребоваться другое действие, например /user/login/facebook, и просто перенаправить на путь HWIOAuthBundle}.
-
Если запрос содержит параметры _username и _password, тогда я
необходимо переслать запрос на регистрацию (я не уверен в этом
один. Могу ли я просто обработать эту форму самостоятельно? Или, если я вручную проверю
имя пользователя и пароль к базе данных?)
-
Добавить прослушиватель событий входа в систему, если пользователь успешно прошел аутентификацию,
генерировать ключ api для пользователя (это необходимо, только если я не проверю его сам).
-
Верните API-ключ в ответ на запрос POST (это прерывает
стратегия post-redirect-get, но в остальном я не вижу никаких проблем
с этим) Я думаю, что это устраняет перенаправление на параметр проверки входа I
перечисленных выше.
Как вы, наверное, видите, я в замешательстве. Это мой первый проект Symfony2, а книги на страницах "Безопасность" просты... но, похоже, замаскировали некоторые детали, и это оставило меня совершенно неуверенным в том, как продолжить.
Спасибо заранее!
=============================================== ==============
Edit:
Я установил аутентификацию ключа API в значительной степени идентично соответствующей статье поваренной книги: http://symfony.com/doc/current/cookbook/security/api_key_authentication.html
Чтобы обрабатывать вход пользователя в систему, я создал собственный метод контроллера. Я сомневаюсь, что это прекрасно, я бы хотел услышать некоторые отзывы о том, как это можно улучшить, но я верю, что я на правильном пути, так как мой поток теперь работает. Вот код (обратите внимание, что еще рано развиваться... Я еще не смотрел учетную запись в Facebook, только простой логин/пароль):
class SecurityController extends FOSRestController
{
/**
* Create a security token for the user
*/
public function tokenCreateAction()
{
$request = $this->getRequest();
$username = $request->get('username',NULL);
$password = $request->get('password',NULL);
if (!isset($username) || !isset($password)){
throw new BadRequestHttpException("You must pass username and password fields");
}
$um = $this->get('fos_user.user_manager');
$user = $um->findUserByUsernameOrEmail($username);
if (!$user instanceof \Acme\UserBundle\Entity\User) {
throw new AccessDeniedHttpException("No matching user account found");
}
$encoder_service = $this->get('security.encoder_factory');
$encoder = $encoder_service->getEncoder($user);
$encoded_pass = $encoder->encodePassword($password, $user->getSalt());
if ($encoded_pass != $user->getPassword()) {
throw new AccessDeniedHttpException("Password does not match password on record");
}
//User checks out, generate an api key
$user->generateApiKey();
$em = $this->getDoctrine()->getEntityManager();
$em->persist($user);
$em->flush();
return array("apiKey" => $user->getApiKey());
}
}
Это работает очень хорошо, и регистрация пользователя будет обрабатываться аналогичным образом.
Интересно, что метод аутентификации ключа api, который я реализовал из кулинарной книги, кажется, игнорирует параметры access_control в моем файле security.yml, в кулинарной книге они описывают, как генерировать токен только для определенного пути, но я не сделал этого, t как это решение, поэтому я внедрил свое собственное (а также немного плохое) решение, чтобы не проверять путь, который я использую для аутентификации пользователей
api_login:
pattern: ^/api/v1/user/authenticate$
security: false
api:
pattern: ^/api/*
stateless: true
anonymous: true
simple_preauth:
authenticator: apikey_authenticator
Я уверен, что есть лучший способ сделать это тоже, но снова... не уверен, что это такое.
Ответы
Ответ 1
Я не думаю, что вам действительно нужна конечная точка /login.
В документе symfony клиент api должен передать его ключ (через параметр http apiKey) каждому запросу API.
Я не уверен в лучшей практике, но вы можете это сделать.
"The book page and bundle both cover authenticating a user by API key, but don't touch on the flow of logging users in, generating API keys, allowing users to register"
Лучше всего разрешить пользователям регистрироваться через веб-форму (например, с помощью маршрута fos_user_register). Пользовательский объект может иметь поле apikey, предварительно заполненное ключом, сгенерированным подобно этому sha1 (например, "secret".time()) и кнопку в своем профиле для восстановления ключа.
Ответ 2
Вы пытаетесь внедрить аутентификацию без сохранения имени пользователя и логин. Это в значительной степени то, что дает пропускная способность аутентификации Oauth2. Это довольно стандартно, поэтому вместо того, чтобы пытаться реализовать его самостоятельно, я бы рекомендовал вам использовать Bundle для этого, например FOSOauthServerBundle. Он может использовать FOSUserBundle в качестве своего поставщика услуг и будет более чистым, более защищенным и более простым в использовании, чем самодельное решение.
Чтобы зарегистрировать пользователя, вы можете создать действие регистра в вашем API (например, в REST API, я бы использовал POST-api/v1/users), а в методе контроллера копировать и проделывать код из FOSUserBundle: RegistrationController (конечно, адаптируйте его для ваших нужд).
Я сделал это в REST API, он работал как шарм.
Ответ 3
Class GenearteToken extends FOSRestController
{
public getTokenAction(Request $request){
$apiKey = $request->query->get('apikey');
return $apiKey;
}
}