duannaozhao4626 2011-01-16 17:53
浏览 72
已采纳

Symfony2项目中基于学说的认证机制

I am working on a small Doctrine2-backed project using Symfony2 for the first time. Currently I'm struggling with the security component of symfony2, to be exact with the authentication mechanism described in the documentation.

I want to use a form-based authentication and did everything stated in the docs:

I have an security.yml configuration file which looks like this:

security.config:
    firewalls:
        admin:
            pattern:                             /admin/.*
            form-login:                          true
            logout:                              true
            login_path:                          /login
            check_path:                          /validateLogin
            always_use_default_target_path:      false
            target_path_parameter:               target
        check_page:
            pattern:                             /validateLogin
            form-login:                          true
            login_path:                          /login
            check_path:                          /validateLogin
            always_use_default_target_path:      false
            target_path_parameter:               target
        public:
            pattern:                             /.*
            security:                            false
    providers:
        admin:
            password_encoder:                    md5
            entity:
                class:                           AdminBundle:User
                property:                        username
    access_control:
        - { path: /admin/.*, role: ROLE_ADMIN }
        - { path: /validateLogin, role: IS_AUTHENTICATED_ANONYMOUSLY }
    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER

The check_page is excluded from the "secureless" area after reading a similar thread on devcomments.

In my routing configuration I include two rules for the authentication:

_security_login:
    pattern:                      /login
    defaults:    
        _controller:              PublicBundle:Auth:index

_security_check:
    pattern:                      /validateLogin

The entity class I am using to represent a user is an Doctrine2 entity and implements the AccountInterface:

<?php

namespace Application\AdminBundle\Entity;

use Symfony\Component\Security\User\AccountInterface;

/**
 * @orm:Entity
 */
class User implements AccountInterface
{
/**
 * @orm:Id
 * @orm:Column(type="integer")
 * @orm:GeneratedValue(strategy="IDENTITY")
 */
protected $id;
/**
 * @orm:Column(type="string", length="255")
 */
protected $username;
/**
 * @orm:Column(type="string", length="40")
 */
protected $password;

public function getId()
{
    return $this->id;
}

public function setId($id)
{
    $this->id = $id;
}

public function getUsername()
{
    return $this->username;
}

public function setUsername($username)
{
    $this->username = $username;
}

public function getPassword()
{
    return $this->password;
}

public function setPassword($password)
{
    $this->password = $password;
}

/**
 * Implementing the AccountInterface interface
 */
public function __toString()
{
    return $this->getUsername();
}

public function getRoles()
{
    return array('ROLE_ADMIN');
}

public function eraseCredentials()
{

}

public function getSalt()
{
    return $this->getId();
}
}

In the class AuthController I am using the example code from the symfony2 documents:

public function indexAction()
{
    if ($this->get('request')->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
        $error = $this->get('request')->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
    } else {
        $error = $this->get('request')->getSession()->get(SecurityContext::AUTHENTICATION_ERROR);
    }

    return
        $this->render(
            'PublicBundle:Auth:index.twig',
            array(
                'last_username' => $this->get('request')->getSession()->get(SecurityContext::LAST_USERNAME),
                'error' => $error));
}

Now comes the problem: The redirection rule from http://symfony2.localhost/app_dev.php/admin/test to http://symfony2.localhost/app_dev.php/login works but after entering username/password and submitting the login form, I am being redirected to the login url again without an error message.

I know that this is probably a really basic issue but since there is not yet much documentation on symfony2, I think this is a good place to ask questions like this one. In general there are some points inside a symfony2 project which seem to be working magically (of course DI-backed) which make the learning process a bit hard. My thoughts on how the authentication works is that there is some magical Controller which catches the validateLogin action, looks for an entity repository for my User entity, calls findOneBy('username' => $username) and compares the passwords... is this right?

Thank you in advance for any hint, I have been googling this issue for more some hours now... :)

Paul

  • 写回答

3条回答 默认 最新

  • dpjuppr1361 2011-01-23 12:42
    关注

    My thoughts on how the authentication works is that there is some magical Controller which catches the validateLogin action, looks for an entity repository for my User entity, calls findOneBy('username' => $username) and compares the passwords... is this right?

    You're wrong. Authentication doesn't involve any controller, that's why you don't specify any in _security_check route. Auth is based on EventDispatcher. Whenever you specify some listener in your firewall (eg. form_login, anonymous, logout etc.) you actually register a new listener for core.security event. Symfony\Component\HttpKernel\Security\Firewall::handle() is a place where these listeners are actually registered.

    The general, simplified flow:

    1. User fills login form (_username and _password fields).
    2. Request is handled by Symfony2.
    3. core.security event is fired.
    4. EventDispatcher notifies all listeners.
    5. UsernamePasswordFormAuthenticationListener is fired (handle() method) and checks whether:
      1. URL matches check_path option.
      2. Request has both _username and _password parameters.
    6. Listener tries to authenticate user (attemptAuthentication() method).
    7. Authentication manager fires all registered providers.
    8. Finally, DaoAuthenticationProvider is fired and it tries to retrieve user using Doctrine's user repository class.
    9. If everything is fine UsernamePasswordToken (which contain $user object returned by loadUserByUsername() method) is being returned and user is redirected.

    Indeed security mechanism is quite complex and hard to understand (documentation isn't still finished). But when you finally understand how it works then you'll see how powerful mechanism it is.


    I wrote my own authentication mechanism and it works fine.

    1. Configuration:

      I'm using custom provider and encoder.

      security.config:
          providers:
              main:
                  id:         project.user_repository # DI id. Doctrine's UserRepositry
                  check_path: /login-check
          encoders:
              main:
                  class: Project\SiteBundle\Entity\User
                  id:    security.encoder.sha512     # DI id. Service %security.encoder.digest.class% (with "sha512" as first parameter)
          firewalls:
              restricted:
                  pattern:    /panel/.*
                  form_login: 
                      check_path: /login-check
              public:
                  pattern:    /.*
                  anonymous:  true
                  form_login: 
                      check_path: /login-check
                  logout:     true
          access_control:
              - { path: /panel/.*, role: ROLE_USER }
              - { path: /.*, role: IS_AUTHENTICATED_ANONYMOUSLY }
      

      As you can see /panel/* is restricted, while /* is public.

    2. Service security.encoder.sha512 is a built-in encoder:

      <service id="security.encoder.sha512" class="%security.encoder.digest.class%">
          <argument>sha512</argument>
      </service>
      
    3. Project\SiteBundle\Entity\User:

      /**
       * @orm:Entity(repositoryClass="Project\SiteBundle\Repository\UserRepository")
       */
      class User implements AdvancedAccountInterface {
          /** 
           * @orm:Id @orm:Column(type="integer")
           * @orm:GeneratedValue(strategy="AUTO")
           */
          protected $id;
      
          /**
           * @orm:Column(unique=true, nullable=true)
           */
          protected $email;
      
          /**
           * @orm:Column(unique=true, nullable=true)
           */
          protected $xmpp;
      
          /**
           * @orm:Column(length=128)
           */
          protected $password;
      
          /**
           * @orm:Column(length=16)
           */
          protected $salt;
      
          // User can be logged in using email address or xmpp adress.
      
          // Dozens of getters/setters here.
      }
      
    4. Project\SiteBundle\Repository\UserRepository

      class UserRepository extends EntityRepository implements UserProviderInterface {
          public function loadUserByUsername($username) {
              $dql = sprintf('
                  SELECT u
                  FROM %s u
                  WHERE u.email = :id OR u.xmpp = :id
              ', $this->_entityName);
      
              $user = null;
      
              try {
                  $user = $this->_em->createQuery($dql)->setParameter('id', $username)->getSingleResult();
              } catch (ORMException $e) {
                  throw new UsernameNotFoundException("User $username not found.", $e->getCode(), $e);
              }
      
              return $user;
          }
      
          public function loadUserByAccount(AccountInterface $user) {
              return $this->loadUserByUsername($user->getUsername());
          }
      }
      
    5. Security routes and controller is same as yours.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(2条)

报告相同问题?

悬赏问题

  • ¥100 set_link_state
  • ¥15 虚幻5 UE美术毛发渲染
  • ¥15 CVRP 图论 物流运输优化
  • ¥15 Tableau online 嵌入ppt失败
  • ¥100 支付宝网页转账系统不识别账号
  • ¥15 基于单片机的靶位控制系统
  • ¥15 真我手机蓝牙传输进度消息被关闭了,怎么打开?(关键词-消息通知)
  • ¥15 装 pytorch 的时候出了好多问题,遇到这种情况怎么处理?
  • ¥20 IOS游览器某宝手机网页版自动立即购买JavaScript脚本
  • ¥15 手机接入宽带网线,如何释放宽带全部速度