使用FOSUserBundle(和HWIOauthBundle)在Symfony2中进行简单的API密钥验证,填补空白

时间:2014-03-28 22:00:24

标签: symfony fosuserbundle fosrestbundle hwioauthbundle

编辑:请参阅下面的我自己的解决方案,在撰写本文时,它正在运作但不完美。我会喜欢一些批评和反馈,如果我把一些我认为非常可靠的东西放在一起,那么我会为面临同样挑战的其他人制作一篇博文博客文章。

我已经挣扎了好几天了,我希望有人能告诉我,如果我走的是正确的道路。

我有一个带有FOSRestBundle网络服务的系统,我目前正在使用FOSUserBundle和HWIOAuthBundle对用户进行身份验证。

我想为webservice设置无状态api密钥身份验证。

我已经阅读了http://symfony.com/doc/current/cookbook/security/api_key_authentication.html,这似乎很容易实现,我还安装了UecodeApiKeyBundle,这似乎只是本书页面的一个实现。

我的问题是n00b ...现在怎么办?书籍页面和软件包都涵盖了通过API密钥对用户进行身份验证,但是不要触及日志记录用户的流程,生成API密钥,允许用户注册等。我真正想要的是简单的API端点我的应用开发者可以使用的登录,注册和注销。像/ api / v1 / login等等。

我想我可以处理注册....虽然登录让我感到困惑。根据一些额外的阅读,在我看来,我需要做的就是登录:

  • 在api / v1 / login创建一个接受POST请求的控制器。该 请求将显示为{_username:foo,_ password:bar}或 像{facebook_access_token:foo。或者,facebook登录可能需要不同的操作,例如/ user / login / facebook,只需重定向到HWIOAuthBundle路径}。

  • 如果请求包含_username和_password参数,那么我 需要转发登录检查请求(我不确定这一点 一。我可以自己处理这个表格吗?或者,我应该手动检查 针对数据库的用户名和密码?)

  • 如果用户成功通过身份验证,请添加登录事件侦听器, 为用户生成一个api密钥(这只有在我自己没有自己检查的情况下才有必要)

  • 在POST请求的响应中返回API密钥(此中断 后重定向获取策略,但我没有看到任何问题 用它)我认为这消除了重定向到登录检查选项我 上面列出的。

你可能会看到我很困惑。这是我的第一个Symfony2项目,关于安全性的书籍页面很简单......但似乎掩盖了一些细节,这使我完全不确定要采取的方法。

提前致谢!

=============================================== ==============

编辑:

我已经安装了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());
    }

}

这看起来效果很好,用户注册的处理也类似。

有趣的是,我从cookbook实现的api密钥身份验证方法似乎忽略了我的security.yml文件中的access_control设置,在cookbook中他们概述了如何仅生成特定路径的令牌,但我没有&# 39; t喜欢这个解决方案,所以我实施了自己的(也有点差)解决方案,不检查我用来验证用户的路径

api_login:
    pattern: ^/api/v1/user/authenticate$
    security: false

api:
    pattern: ^/api/*
    stateless: true
    anonymous: true
    simple_preauth:
        authenticator: apikey_authenticator

我确信有更好的方法可以做到这一点,但又一次......不确定它是什么。

3 个答案:

答案 0 :(得分:3)

您正尝试使用用户名和登录名实施无状态身份验证。这几乎是what the Oauth2 authentication passsword grant does。这是非常标准的,所以我建议您不要试图自己实现它,而是使用Bundle,例如FOSOauthServerBundle。它可以使用FOSUserBundle作为其用户提供商,并且比自制解决方案更清洁,更安全,更易于使用。

要注册用户,您可以在API中创建注册操作(例如,在REST API中我使用POST - api / v1 / users),并在控制器方法中复制并通过FOSUserBundle中的代码: RegistrationController(当然会根据您的需要进行调整)。

我在REST API中做到了这一点,它就像一个魅力。

答案 1 :(得分:2)

我认为你真的不需要/ login端点。

在symfony doc中,api客户端需要将其密钥(通过apiKey http参数)传递给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"

最好是允许您的用户通过Web表单注册(例如使用路由fos_user_register)。用户实体可以有一个apikey字段,预先填充了一个像sha1(“secret”.time())生成的密钥,并在其配置文件中有一个按钮来重新生成密钥。

答案 2 :(得分:-1)

Class GenearteToken extends FOSRestController
{

     public getTokenAction(Request $request){

          $apiKey = $request->query->get('apikey');
          return $apiKey;
      }


}