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

Pez*_*Pez 12 symfony fosuserbundle fosrestbundle hwioauthbundle

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

几天来我一直在努力奋斗,如果我走在正确的道路上,我希望有人能告诉我.

我有一个带有FOSRestBundle webservice的系统,我正在使用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参数,那么我需要将请求转发到login-check(我不确定这个.我可以自己处理这个表单吗?或者,我应该手动检查用户名和密码数据库?)

  • 添加一个登录事件监听器,如果用户认证成功,则为用户生成一个api密钥(当然,如果我自己没有检查它,这是必要的)

  • 在POST请求的响应中返回API密钥(这会打破post-redirect-get策略,但是我没有看到任何问题)我认为这消除了上面列出的重定向登录检查选项.

你可能会看到我很困惑.这是我的第一个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());
    }

}
Run Code Online (Sandbox Code Playgroud)

这似乎工作得很好,用户注册将以类似方式处理.

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

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

api:
    pattern: ^/api/*
    stateless: true
    anonymous: true
    simple_preauth:
        authenticator: apikey_authenticator
Run Code Online (Sandbox Code Playgroud)

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

Bac*_*sme 2

我认为您实际上并不真正需要 /login 端点。

在 symfony 文档中,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"
Run Code Online (Sandbox Code Playgroud)

最好的方法是允许您的用户通过网络表单进行注册(例如使用路线 fos_user_register)。用户实体可以有一个 apikey 字段,预先填充了像 sha1("secret".time()) 这样生成的密钥,并且在其配置文件中有一个用于重新生成密钥的按钮。