【总结】Symfony登陆搭建

Posted on Posted in 计算机1,030 views

2016-01-12

byxiaoxia 

一、访问控制【access_control】

1.1、机制

        主要根据url分层控制,你用匹配,指定某一类url只能特定角色的用户使用。

1.2、配置例子

     在默认配置基础上添加如下域:

security:
    access_control:
        - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/login_check, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/, role:[ROLE_STUDENT,ROLE_TEACHER,ROLE_ADMIN] }

1.3、效果

1452574771222935.png

1.4、注意

        1、有些页面必须是所有用户都能访问的,如:loginlogin_check

        2、用户指定时可以指定用户的数组;

二、关于密码源【provider】

    上面提出来路径控制,但是用户和密码哪儿来的呢,在symfony中,是通过provider来提供的。也可以有不同的数据源指定。

2.1、security硬密码

2.1.1、机制

    直接将密码写到security文件中。一般不会这么用,不做详细介绍。

2.1.2、配置实例

  security:
        providers:
            in_memory:
                memory:
                    users:
                        xia:
                            password: xia
                            roles: 'ROLE_USER'
                        admin:
                            password: xiaoxia
                            roles: 'ROLE_ADMIN'
        encoders:
                Symfony\Component\Security\Core\User\User: plaintext
        firewalls:
            dev:
                pattern:  ^/(_(profiler|wdt)|css|images|js)/
                security: false
            main:
                pattern:    ^/
                http_basic: ~
                provider: in_memory

2.1.3、效果

无标题2.png

2.2、数据库动态密码-Entity

2.2.1、数据库

        1、设计设计

'id',       'int(11)',      'NO',     'PRI',  NULL,     'auto_increment'
'username', 'varchar(45)',  'NO',     'UNI',  NULL,     ''
'password', 'varchar(256)', 'NO',     '',     NULL,     ''
'role',     'varchar(45)',  'YES',    '',     BULL,     ''
'rid',      'varchar(45)',  'YES',    '',     NULL,     ''
'nickname', 'varchar(45)',  'YES',    '',     NULL,     ''
'currentIp', 'varchar(45)', 'YES',    '',     NULL,     ''

        2、生成Entity,根据实际情况而定

app/console doctrine:mapping:import --force CellcomDBBundle yml
app/console doctrine:generate:entities CellcomDBBundle --no-backup

2.2.2、机制

    如果使用了doctrine作为数据持久层(ORM),这可以可以直接讲数据表的UserEntity作为数据源。不过需要做一些修改:

        1、让UserEntity类实现UserInterface接口;

        2、接口有:getRoles()getPassword()getSalt()getUsername()eraseCredentials()

        3、必要时也可以实现Serializable接口,serialize()unserialize()

2.2.3、实例

        1、修改UserEntity类,只列出来修改部分:

<?php
namespace Cellcom\DBBundle\Entity;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\ORM\Mapping as ORM;
class User implements UserInterface, \Serializable
{
    /** Fonctions UserInterface*/
    public function eraseCredentials(
    {
    }
    public function getRoles()
    {
        return (Array)$this->role;
    }
    public function equals(UserInterface $user)
    {
        return $user->getUsername() === $this->username;
    }
    //public function getUsername() 已经自动生成
    //public function getPassword()                 已经自动生成
    public function isEnabled()
    {
        return true;
    }
    public function getSalt()
    {
        return '';
    }
    /** @see \Serializable::serialize() */
    public function serialize()
    {
        return serialize(array(
                  $this->id,
                  $this->username,
                  $this->password,
                  // see section on salt below
                  // $this->salt,
       ));
    }
    /** @see \Serializable::unserialize() */
    public function unserialize($serialized)
    {
        list (
                  $this->id,
                  $this->username,
                  $this->password,
                  // see section on salt below
                  // $this->salt
        ) = unserialize($serialized);
    }

         2security配置:

 security:
            providers:
                cellcom_db_provider:
                    entity:
                        class: CellcomDBBundle:User
                        property: username
            encoders:
                Cellcom\DBBundle\Entity\User: 
                    algorithm:  bcrypt
            firewalls:
                dev:
                    pattern:  ^/(_(profiler|wdt)|css|images|js)/
                    security: false
                main:
                    pattern:    ^/
                    http_basic: ~
                    provider: cellcom_db_provider
                    anonymous: ~
            access_control:
                - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
                - { path: ^/login_check,roles: IS_AUTHENTICATED_ANONYMOUSLY }
                - { path: ^/, role:[ROLE_STUDENT,ROLE_TEACHER,ROLE_ADMIN] }

2.2.4、效果

无标题3.png

2.2.5、注意

    数据库User表可以灵活设计,字段名没有要求,UserInterface中的接口实现的可以做相应的调整。但是如果数据表中设计成:usernamepasswordroles等时,上面接口的实现可以通过doctrine命令自动生成相应的代码,减少工作量,但会牺牲灵活性。

2.3、数据库动态密码-UserProvider

    上面介绍的2.2其实是框架自动做了很多事情,但是是封闭的,不便于扩展。比如记录当前登陆用户记录到数据库中。为此我们可以自定义UserProvider来实现。

2.3.1、数据库

    同2.2.1

2.3.2、机制

    这种情况下需要实现两个接口:UserInterfaceUserProviderInterface

        UserInterface可以使用2.2中的Entity,也可以自定义一个类,只需要实现相应的接口就可以,接口在2.1.1中已经介绍了。这样在系统架构时就不一定需要ORM层了。

        UserProviderInterface主要是为登陆提供相应的数据,需要实现的接口是:loadUserByUsername()refreshUser()supportsClass()

2.3.3、实例

        1UserInterfaceUserEntity

        同2.2.3     

        2UserProviderInterfaceUserProvider

<?php
namespace Cellcom\Service\User;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\LockedException;
use Cellcom\Service\Common\ServiceKernel;
use Cellcom\Service\Common\BaseService;
use Cellcom\DBBundle\Entity\User;
class UserProvider extends BaseService implements UserProviderInterface 
{
    public function __construct ($container)
    {
        $this->container = $container;
    }
    public function loadUserByUsername ($username) 
    {
        $user = $this->getService('cellcom.user')->getUserByLoginField($username);
        if (empty($user)) {
            throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username));
        }
        $user->setCurrentip($this->container->get('request')->getClientIp());
        ServiceKernel::instance()->setCurrentUser($user);
        $this->getService('cellcom.user')->updateUser($user);
        return $user;
    }
    public function refreshUser (UserInterface $user) 
    {
        if (! $user instanceof User) 
        {
            throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
        }
        return $this->loadUserByUsername($user->getUsername());
    }
    public function supportsClass ($class) 
    {
        return $class === 'Cellcom\DBBundle\Entity\User';
    }
}

        3UserService服务

<?php
namespace Cellcom\Service\User;
use Cellcom\Service\Common\BaseService;
class UserService extends BaseService
{
    public function getUserByLoginField($username)
    {
        if(!isset($username))
            return null;
        $user = $this->getDBRepository('CellcomDBBundle:User')->findOneByUsername($username);
        return !isset($user) ? null : $user;
    }
    public function updateUser($user)
    {
        if (!$user) {
            return false;
        }
        $this->getEM()->persist($user);
        $this->getEM()->flush();
    }
}

         4services服务配置

  services: 
        cellcom.user:
            class: Cellcom\Service\User\UserService
            arguments: []
        cellcom.user_provider:
            class: Cellcom\Service\User\UserProvider
            arguments: [@service_container]

         5security配置

security:
        providers:
            cellcom_db_provider:
                id: cellcom.user_provider
        encoders:
            Cellcom\DBBundle\Entity\User: 
                algorithm:  bcrypt
        firewalls:
            dev:
                pattern:  ^/(_(profiler|wdt)|css|images|js)/
                security: false
            main:
                pattern:    ^/
                http_basic: ~
                provider: cellcom_db_provider
                anonymous: ~
        access_control:
            - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
            - { path: ^/login_check, roles: IS_AUTHENTICATED_ANONYMOUSLY }
            - { path: ^/, role:[ROLE_STUDENT,ROLE_TEACHER,ROLE_ADMIN] }

2.3.4、效果

无标题4.png

2.3.5、注意

                  这种方法比较灵活,注意UserInterface中提供的get属性可以在twig{{ app.user.nickname }}来直接调用。

三、关于认证【待补充】

3.1

 

四、登陆页面

4.1、机制

                  实现login路由的接口即可,比较简单。

4.2、实例

                  1security配置:

 security:
        providers:
            cellcom_db_provider:
                id: cellcom.user_provider
        encoders:
            Cellcom\DBBundle\Entity\User: 
                algorithm:  bcrypt
        firewalls:
            dev:
                pattern:  ^/(_(profiler|wdt)|css|images|js)/
                security: false
            main:
                pattern:    ^/
                http_basic: ~
                provider: cellcom_db_provider
                anonymous: ~
                form_login:
                    login_path: login
                    check_path: login_check
        access_control:
            - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
            - { path: ^/login_check, roles: IS_AUTHENTICATED_ANONYMOUSLY }
            - { path: ^/, role:[ROLE_STUDENT,ROLE_TEACHER,ROLE_ADMIN] }

         2Controller

<?php
namespace Cellcom\WebBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Security;
class SecurityController extends Controller
{
    public function loginAction(Request $request)
    {
        $session = $request->getSession();
        // get the login error if there is one
        if ($request->attributes->has(Security::AUTHENTICATION_ERROR)) {
            $error = $request->attributes->get(Security::AUTHENTICATION_ERROR);
        } elseif (null !== $session && $session->has(Security::AUTHENTICATION_ERROR)) {
            $error = $session->get(Security::AUTHENTICATION_ERROR);
            $session->remove(Security::AUTHENTICATION_ERROR);
        } else {
                $error = '';
        }
        // last username entered by the user
        $lastUsername = (null === $session) ? '' : $session->get(Security::LAST_USERNAME);
        return $this->render(
                  'CellcomWebBundle:Security:login.html.twig',
                   array(
                              'last_username' => $lastUsername,
                              'error'         => $error,
                  ));
        }
}

         3twig页面

{% if error %}
   <div>{{ error.message }}</div>
{% endif %}
<form action="{{ path('login_check') }}" method="post">
    <label for="username">Username:</label>
    <input type="text" id="username" name="_username" value="{{ last_username }}" />
    <label for="password">Password:</label>
    <input type="password" id="password" name="_password" />
    <button type="submit">login</button>
</form>

4.3、效果

无标题5.png

4.4、注意

    表单中的name="_username"name="_password",不过也可以自己配置,不做多介绍。

五、参考:

    1security中文介绍:http://www.newlifeclan.com/symfony/archives/242

    2DB作为数据源:http://symfony.com/doc/2.7/cookbook/security/entity_provider.html

    3、登陆表单:http://symfony.com/doc/current/cookbook/security/form_login_setup.html

    4、认证:http://symfony.com/doc/2.7/cookbook/security/custom_authentication_provider.html

 


转载标明出处:https://blog.evanxia.com/2016/02/77