<?php namespace AppBundle\Document; use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB; use Symfony\Component\Security\Core\User\UserInterface; /** * @MongoDB\Document(collection="user") */ class User implements UserInterface, \Serializable { /** * @MongoDB\Id(strategy="auto") */ private $id; /** * @MongoDB\Field(type="string") */ private $username; /** * @MongoDB\Field(type="string") */ private $password; /** * @MongoDB\Field(type="string") */ private $email; /** * @var boolean * @MongoDB\Field(type="boolean") */ private $isActive; ....
<?php namespace AppBundle\Provide; use Doctrine\ODM\MongoDB\DocumentManager; use Symfony\Component\Security\Core\Exception\UnsupportedUserException; use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; class UserProvide implements UserProviderInterface { /** * @var DocumentManager */ protected $dm; public function __construct(DocumentManager $dm) { $this->dm = $dm; } /** * Loads the user for the given username. * * This method must throw UsernameNotFoundException if the user is not * found. * * @param string $username The username * * @return UserInterface * * @throws UsernameNotFoundException if the user is not found */ public function loadUserByUsername($username) { $user = $this->dm->getRepository("AppBundle:User")->findOneBy(array('username'=>$username)); if ($user) { return $user; } throw new UsernameNotFoundException( sprintf('Username "%s" does not exist.', $username) ); } /** * Refreshes the user for the account interface. * * It is up to the implementation to decide if the user data should be * totally reloaded (e.g. from the database), or if the UserInterface * object can just be merged into some internal array of users / identity * map. * * @param UserInterface $user * * @return UserInterface * * @throws UnsupportedUserException if the account is not supported */ public function refreshUser(UserInterface $user) { if (!$user instanceof UserInterface) { throw new UnsupportedUserException( sprintf('Instances of "%s" are not supported.', get_class($user)) ); } return $this->loadUserByUsername($user->getUsername()); } /** * Whether this provider supports the given user class. * * @param string $class * * @return bool */ public function supportsClass($class) { return $class === 'AppBundle\Document\User'; } }
// services.yml app.user.provide: class: AppBundle\Provide\UserProvide arguments: - "@doctrine.odm.mongodb.document_manager"
###1.TestUserphp
namespace AppBundle\Document; use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB; use Symfony\Component\Security\Core\User\UserInterface; /** * @MongoDB\Document(collection="test_user") */ class TestUser implements UserInterface, \Serializable { /** * @MongoDB\Id(strategy="auto") */ private $id; /** * @MongoDB\Field(type="string") */ private $username; /** * @MongoDB\Field(type="string") */ private $password; /** * @MongoDB\Field(type="string") */ private $email; ...
###2. TestUserProvidecss
<?php namespace AppBundle\Provide; use Doctrine\ODM\MongoDB\DocumentManager; use Symfony\Component\Security\Core\Exception\UnsupportedUserException; use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; class TestUserProvide implements UserProviderInterface { /** * @var DocumentManager */ protected $dm; public function __construct(DocumentManager $dm) { $this->dm = $dm; } /** * Loads the user for the given username. * * This method must throw UsernameNotFoundException if the user is not * found. * * @param string $username The username * * @return UserInterface * * @throws UsernameNotFoundException if the user is not found */ public function loadUserByUsername($username) { $user = $this->dm->getRepository("AppBundle:TestUser")->findOneBy(array('username'=>$username)); if ($user) { return $user; } throw new UsernameNotFoundException( sprintf('Username "%s" does not exist.', $username) ); } /** * Refreshes the user for the account interface. * * It is up to the implementation to decide if the user data should be * totally reloaded (e.g. from the database), or if the UserInterface * object can just be merged into some internal array of users / identity * map. * * @param UserInterface $user * * @return UserInterface * * @throws UnsupportedUserException if the account is not supported */ public function refreshUser(UserInterface $user) { if (!$user instanceof UserInterface) { throw new UnsupportedUserException( sprintf('Instances of "%s" are not supported.', get_class($user)) ); } return $this->loadUserByUsername($user->getUsername()); } /** * Whether this provider supports the given user class. * * @param string $class * * @return bool */ public function supportsClass($class) { return $class === 'AppBundle\Document\TestUser'; } }
//services.yml app.test_user.provide: class: AppBundle\Provide\TestUserProvide arguments: - "@doctrine.odm.mongodb.document_manager"
<?php namespace AppBundle\Security\Authentication\Token; use Symfony\Component\Security\Core\Authentication\Token\AbstractToken; class TestToken extends AbstractToken { /** * @param array $roles */ public function __construct(array $roles = array()) { parent::__construct($roles); $this->setAuthenticated(count($roles) > 0); } /** * Returns the user credentials. * * @return mixed The user credentials */ public function getCredentials() { return ''; } }
###4.建立自定義Authentication Providemongodb
<?php namespace AppBundle\Security\Authentication\Provide; use AppBundle\Security\Authentication\Token\TestToken; use AppBundle\Security\TestUserAuthenticate; use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\User\UserProviderInterface; class TestProvide implements AuthenticationProviderInterface { protected $userProvider; protected $testUserAuthenticate; /** * @param UserProviderInterface $userProvider */ public function __construct(UserProviderInterface $userProvider,TestUserAuthenticate $testUserAuthenticate) { $this->userProvider = $userProvider; $this->testUserAuthenticate = $testUserAuthenticate; } /** * Attempts to authenticate a TokenInterface object. * * @param TokenInterface $token The TokenInterface instance to authenticate * * @return TokenInterface An authenticated TokenInterface instance, never null * * @throws AuthenticationException if the authentication fails */ public function authenticate(TokenInterface $token) { $user = $this->userProvider->loadUserByUsername($token->getUsername()); if ($user && $this->testUserAuthenticate->isAuthenticationValid($user,$token->getUsername(),$token->getCredentials())) { $authenticatedToken = new TestToken($user->getRoles()); $authenticatedToken->setUser($user); return $authenticatedToken; } throw new AuthenticationException('The test authentication failed.'); } /** * Checks whether this provider supports the given token. * * @param TokenInterface $token A TokenInterface instance * * @return bool true if the implementation supports the Token, false otherwise */ public function supports(TokenInterface $token) { return $token instanceof TestToken || $token instanceof UsernamePasswordToken ; } }
###5.驗證輸入的密碼是否正確(注意上面第4步isAuthenticationValid方法)數據庫
<?php namespace AppBundle\Security; use Doctrine\ODM\MongoDB\DocumentManager; use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; class TestUserAuthenticate { /** * @var EncoderFactoryInterface */ protected $encoderFactory; /** * @var DocumentManager */ protected $dm; public function __construct(DocumentManager $dm,EncoderFactoryInterface $encoderFactory) { $this->dm = $dm; $this->encoderFactory = $encoderFactory; } public function isAuthenticationValid($user,$username,$password) { $password = $this->encoderFactory->getEncoder($user)->encodePassword($password,$user->getSalt()); $result = $this->dm->getRepository('AppBundle:TestUser')->findOneBy(array('username'=>$username,'password'=>$password)); if(empty($result)){ return false; }else{ return true; } } }
<?php namespace AppBundle\DependencyInjection\Security\Factory; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FormLoginFactory; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\Reference; class TestFactory extends FormLoginFactory { /** * @param ContainerBuilder $container * @param string $id * @param array $config * @param string $userProviderId * @return string */ protected function createAuthProvider(ContainerBuilder $container, $id, $config, $userProviderId) { $providerId = 'security.authentication.provider.test.' . $id; $container->setDefinition($providerId, new DefinitionDecorator('app.test.security.authentication.provider')) ->replaceArgument(0, new Reference($userProviderId)); return $providerId; } /** * @return string */ public function getPosition() { return 'form'; } /** * @return string */ public function getKey() { return 'test-login'; } }
<?php namespace AppBundle; use AppBundle\DependencyInjection\Security\Factory\TestFactory; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; class AppBundle extends Bundle { public function build(ContainerBuilder $container) { parent::build($container); $extension = $container->getExtension('security'); $extension->addSecurityListenerFactory(new TestFactory()); } }
services: app.user.provide: class: AppBundle\Provide\UserProvide arguments: - "@doctrine.odm.mongodb.document_manager" app.test.authentivate: class: AppBundle\Security\TestUserAuthenticate arguments: - "@doctrine.odm.mongodb.document_manager" - "@security.encoder_factory" app.test_user.provide: class: AppBundle\Provide\TestUserProvide arguments: - "@doctrine.odm.mongodb.document_manager" app.test.security.authentication.provider: class: AppBundle\Security\Authentication\Provide\TestProvide arguments: - '' - '@app.test.authentivate'
security: encoders: AppBundle\Document\User: plaintext AppBundle\Document\TestUser: plaintext providers: chain_provider: chain: providers: [user_provider,test_user_provider] user_provider: id: app.user.provide test_user_provider: id: app.test_user.provide firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false main: test_login: provider: test_user_provider login_path: login check_path: login default_target_path: homepage form_login: provider: user_provider login_path: login check_path: login default_target_path: homepage logout: path: logout target: login anonymous: ~
按照上面的步驟就能實如今一個輸入框經過兩種不一樣的方式登陸。若是在項目運行的過程當中,只須要本地的User,這個實現也很簡單,只須要在自定義的第三方的UserProvide裏面返回本地User就能夠了,思路以下:app
// TestUserProvide public function loadUserByUsername($username) { //$user = $this->dm->getRepository("AppBundle:TestUser")->findOneBy(array('username'=>$username)); //if ($user) { // return $user; //} //1.驗證輸入的用戶名在第三方系統中正確,並返回用戶信息(TestUser) //2.查看本地數據庫是否存在該用戶名的用戶,若是有直接返回User,沒有,根據TestUser的信息新建一個User,存儲到本地,返回新建立的User //3.此時雖然不一樣用戶登陸時使用的Token不同,可是$token->getUser()取的都是本地User對象的實例 throw new UsernameNotFoundException( sprintf('Username "%s" does not exist.', $username) ); }