Symfony2でSNSサイトなどを作ろうと思うと必ず認証関係が必要になって、FOSUserBundle & FOSFacebookBundleでユーザ認証サイトを作ることになります。
https://github.com/FriendsOfSymfony/FOSUserBundle
https://github.com/FriendsOfSymfony/FOSFacebookBundle
しかし、このFOSUserBundle & FOSFacebookBundleで認証というのが結構クセモノでなかなかうまくベース作りができません。
OAuthを理解していてもSymfony2でのroutingやbundle内部の仕組みを理解するまでにかなりの時間がかかります。
FOSUserBundleは思った以上に簡単に実装ができます。問題はFOSFacebookBundleとの共存です。
symfony2.1での例になっています。symfony2.0ではprovider_chainなどが無くなっていますので注意してください。
一度では全部書ききれないので、まずマニュアル等には書いていないノウハウ的なことをとにかくリスト化します。あとはgithubのソースを読んでください。
1.config.ymlの基本設定
ローカルサイトなどで作成するとOAuthなどの認証が通らずエラーが出るので必ず公開ドメインでまず実験を
https://github.com/FriendsOfSymfony/FOSFacebookBundle
config.ymlとsecurity.ymlはしっかりと。
2.UserBundle, FacebookBundleは共通のログイン画面
UserBundle, FacebookBundleは共通のログイン画面(/login)になります。/loginのログイン画面のfacebookのログインボタンを押すとFacebook→OAuth→check_path: /login_check_facebookで認証の流れになります。
3._security_checkはダミーのコントローラーで処理
http://stackoverflow.com/questions/9046669/fosfacebookbundle-and-fosuserbundle
facebookBundleの_security_check(routing.yml)はpattern: /login_check_facebookなど別名にしダミーのコントローラーで処理します。 /login_checkはUserBundleで利用します。/login_check_facebookのコントローラーはダミーで大丈夫です。
4.FaceboouBundleのユーザ名は標準でfacebookId
facebookでログインしてくるユーザのユーザ名は標準ではfacebookIdになる(User modelのsetFbdataを参照)。
facebookでログインするユーザ名をfacebookのユーザ名にするにはUser modelやFacebookProvider.phpを参照
5.FacebookBundleのvalidation
FacebookとUserBundleで同一メールアドレスやユーザ名の場合はvalidationが通りません。validation.xmlを自前で準備しFacebook validation_groupsを追加する必要があります。
これにははまりました。FacebookProvider.phpの
if (count($this->validator->validate($user, 'Facebook'))) { // TODO: the user was found obviously, but doesnt match our expectations, do something smart throw new UsernameNotFoundException('The facebook user could not be stored'); }
Facebookというvalidation groupsは設定されていないのでfacebookを追加して自前で準備します。
<?xml version="1.0" ?> <constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd"> <class name="FOS\UserBundle\Model\User"> <constraint name="Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity"> <option name="fields">usernameCanonical</option> <option name="errorPath">username</option> <option name="message">fos_user.username.already_used</option> <option name="groups"> <value>Registration</value> <value>Profile</value> <value>Facebook</value> </option> </constraint> <constraint name="Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity"> <option name="fields">emailCanonical</option> <option name="errorPath">email</option> <option name="message">fos_user.email.already_used</option> <option name="groups"> <value>Registration</value> <value>Profile</value> <value>Facebook</value> </option> </constraint> </class> </constraint-mapping>
6.success_handler
ログインした後にロールを見てリダイレクト!などの処理はsecurity.ymlのsuccess_handlerを利用します。
Symfony/Component/Security/Http/Authentication
AuthenticationSuccessHandlerInterface onAuthenticationSuccessを実装します。
config.ymlでのinjectionも忘れずに。 RouterInterface $router,SecurityContext $securityをコンストラクタに投げているので [“@router”, “@security.context”] をconfig.ymlでインジェクトしてください。
<?php namespace App\UserBundle\Handler; use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Routing\RouterInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\Routing\Router; use Symfony\Component\Security\Core\SecurityContext; use Symfony\Component\Security\Core\Exception\AuthenticationException; class AuthenticationHandler implements AuthenticationSuccessHandlerInterface, AuthenticationFailureHandlerInterface { protected $router; protected $security; public function __construct(RouterInterface $router,SecurityContext $security) { $this->router = $router; $this->security = $security; } public function onAuthenticationSuccess(Request $request, TokenInterface $token) { return new RedirectResponse($this->router->generate('fos_user_profile')); } public function onAuthenticationFailure(Request $request, AuthenticationException $exception) { return new Response(); } }
あとはgithubのソースで。
https://github.com/kmusiclife/App