14

私は、初めて Symfony2 を使用して、Doctrine2 に支援された小さなプロジェクトに取り組んでいます。現在、symfony2 のセキュリティ コンポーネント、正確にはドキュメントに記載されている認証メカニズムに苦労しています。

フォームベースの認証を使用したいのですが、ドキュメントに記載されているすべてのことを行いました:

次のような security.yml 構成ファイルがあります。

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

devcommentsで同様のスレッドを読んだ後、 check_page は「セキュアレス」領域から除外されます。

私のルーティング構成には、認証用の 2 つのルールが含まれています。

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

_security_check:
    pattern:                      /validateLogin

ユーザーを表すために使用しているエンティティ クラスは Doctrine2 エンティティであり、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();
}
}

クラス AuthController では、symfony2 ドキュメントのサンプル コードを使用しています。

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

ここで問題が発生します: http://symfony2.localhost/app_dev.php/admin/test からhttp ://symfony2.localhost/app_dev.php/loginへのリダイレクトルールは機能しますが、ユーザー名/パスワードを入力してログインフォームを送信した後、エラー メッセージなしでログイン URL に再度リダイレクトされます。

これはおそらく非常に基本的な問題であることはわかっていますが、symfony2 に関するドキュメントはまだあまりないため、このような質問をするのに適した場所だと思います。一般に、symfony2 プロジェクト内には魔法のように (もちろん DI に裏打ちされた) 動作しているように見えるいくつかのポイントがあり、学習プロセスを少し難しくしています。認証がどのように機能するかについての私の考えは、validateLogin アクションをキャッチし、User エンティティのエンティティ リポジトリを探し、findOneBy('username' => $username) を呼び出し、パスワードを比較する魔法の Controller があるということです...これは右?

ヒントを事前にありがとう、私はこの問題をさらに数時間グーグルで調べています... :)

ポール

4

3 に答える 3

20

認証がどのように機能するかについての私の考えは、validateLogin アクションをキャッチし、User エンティティのエンティティ リポジトリを探し、findOneBy('username' => $username) を呼び出し、パスワードを比較する魔法の Controller があるということです...これは右?

あなたが間違っている。認証にはコントローラーは関与しません。そのため、_security_checkルート内で何も指定しません。Auth は に基づいていEventDispatcherます。ファイアウォールで何らかのリスナー (例: 、 など) を指定するform_loginanonymouslogout実際にはcore.securityイベントの新しいリスナーが登録されます。Symfony\Component\HttpKernel\Security\Firewall::handle()これらのリスナーが実際に登録される場所です。

一般的な単純化されたフロー:

  1. ユーザーがログイン フォーム (_usernameおよび_passwordフィールド) に入力します。
  2. リクエストは Symfony2 によって処理されます。
  3. core.securityイベントが発生します。
  4. EventDispatcher はすべてのリスナーに通知します。
  5. UsernamePasswordFormAuthenticationListener( メソッド) が起動handle()され、次のことを確認します。
    1. URL 一致check_pathオプション。
    2. リクエストには_username_passwordパラメータの両方があります。
  6. attemptAuthentication()リスナーはユーザー (メソッド)を認証しようとします。
  7. 認証マネージャーは、登録されているすべてのプロバイダーを起動します。
  8. 最後にDaoAuthenticationProviderが起動され、Doctrine のユーザー リポジトリ クラスを使用してユーザーを取得しようとします。
  9. すべてが正常な場合(メソッドによって返されたオブジェクトUsernamePasswordTokenを含む) が返され、ユーザーがリダイレクトされます。$userloadUserByUsername()

実際、セキュリティ メカニズムは非常に複雑で理解しにくいものです (ドキュメントはまだ完成していません)。しかし、最終的にそれがどのように機能するかを理解すると、それがいかに強力なメカニズムであるかがわかります.


独自の認証メカニズムを作成しましたが、正常に動作します。

  1. 構成:

    カスタム プロバイダーとエンコーダーを使用しています。

    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 }
    

    ご覧のとおり/panel/*、は制限されていますが、/*公開されています。

  2. Servicesecurity.encoder.sha512は組み込みのエンコーダーです。

    <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. セキュリティルートとコントローラーはあなたのものと同じです。

于 2011-01-23T12:42:29.527 に答える
4

You should use the https://github.com/FriendsOfSymfony/FOSUserBundle FOS UserBundle, it implements all this with Doctrine 2 and has tons of features.

于 2011-01-19T22:42:40.230 に答える
1

基本的に、エラー メッセージが表示されずにログイン ページが再度読み込まれる理由は、皮肉なことに、セキュリティ設定がログイン ページへの匿名アクセスを許可するように設定されていないためです。

于 2011-01-30T23:36:07.647 に答える