一个登录框实现不同的登录验证

一.本地user登录

1. 定义一个User

<?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;
    ....

2. 自定义user provide

    <?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"

二. 其他方式认证(这里用另一个user表验证)

###1.TestUser

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. TestUserProvide

<?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"

3.创建自定义Authentication Token

<?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 Provide

<?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;
        }
    }
}

6.创建自己的factory

<?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';
    }
}

7.在security context增加自定义的factory

<?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());
    }
}

8.完整的services.yml

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'

9.完整的security.yml

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就可以了,思路如下:

// 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)
        );
    }

转载于:https://my.oschina.net/u/925519/blog/685087

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值