dougan1465 2015-07-02 00:14
浏览 27

Silex SecurityServiceProvider在尝试进行身份验证时将AnonymousToken存储在HHVM上

I'm using Silex framework on HHVM and running into some issues when trying to implement a SecurityServiceProvider for login.

When trying to perform a login action (with a correct username & password), I'm redirected to the login page instead of the / page. This is because of the following access rule:

$app['security.access_rules'] = array(
    array('^/$', 'ROLE_USER')
);

I've tried dumping $app['security.token_storage']->getToken() and $app['security.token_storage']->getToken()->getUser() at the top of the login page:

object(Symfony\Component\Security\Core\Authentication\Token\AnonymousToken)#350 (5) { 
    ["key":"Symfony\Component\Security\Core\Authentication\Token\AnonymousToken":private]=> string(3) "all" 
    ["user":"Symfony\Component\Security\Core\Authentication\Token\AbstractToken":private]=> string(5) "anon." 
    ["roles":"Symfony\Component\Security\Core\Authentication\Token\AbstractToken":private]=> array(0) { } 
    ["authenticated":"Symfony\Component\Security\Core\Authentication\Token\AbstractToken":private]=> bool(true) 
    ["attributes":"Symfony\Component\Security\Core\Authentication\Token\AbstractToken":private]=> array(0) { } }

The username shows as anon. which isn't my username (test).

Through hacky debugging, I can confirm that:

  1. A User object is being created.
  2. The password is being verified, and returning true.

Relevant parts of the application code (it's roughly based on @mpm's Silex MVC -- haven't confirmed yet if his login code produces better results):

Controller route:

  public function login(Application $app)
  {
    $form = $app['form.factory']->createBuilder('form')
                                ->add('username', 'text',     array('label' => 'Username'))
                                ->add('password', 'password', array('label' => 'Password'))
                                ->getForm();

    return $app['twig']->render('user/login.tpl', array(
      'title' => "Login",
      'form'  => $form->createView(),
      'error' => $app['security.last_error']($app['request'])
    ));
  }

Login template:

{% block content %}
  <h1>Login</h1>

  {% if error %}
    <div>
      {{ error }}
    </div>
  {% endif %}

  <form action="{{ path('user_login_check') }}" method="post" novalidate {{ form_enctype(form) }} class="form-vertical">
    {{ form_widget(form) }}
    <button type="submit">Login</button>
  </form>
{% endblock %}

Security-related service declarations:

$app->register(new SecurityServiceProvider(), array(
  "security.firewalls" => array(
    // All other URLs require authentication.
    "all" => array(
      "pattern" => '^/.*$',
      "form"    => array(
        "login_path"          => '/user/login',
        "check_path"          => '/user/login_check',
        "default_target_path" => '/',
        "username_parameter"  => 'form[username]',
        "password_parameter"  => 'form[password]'
      ),
      "anonymous" => true,
      "logout"    => array(
        "logout_path" => "/user/logout"
      ),
      "users" => $app->share(function () use ($app) {
        return new UserProvider($app['db']);
      })
    )
  )
));

$app['security.encoder.digest'] = $app->share(function ($app) {
  return new PasswordEncoder($app['config']['security']['bcrypt_cost']);
});

$app['security.role_hierarchy'] = array(
  "ROLE_ADMIN"  => array(
    "ROLE_USER",
    "ROLE_CREATE_CHARACTER",
    "ROLE_CREATE_ORIGIN",
    "ROLE_CREATE_COMMISSION",
    "ROLE_DELETE_CHARACTER",
    "ROLE_DELETE_ORIGIN",
    "ROLE_DELETE_COMMISSION"
  ),
  "ROLE_ARTIST" => array(
    "ROLE_USER",
    "ROLE_CREATE_COMMISSION"
  ),
  "ROLE_WRITER" => array(
    "ROLE_USER",
    "RULE_CREATE_ORIGIN"
  )
);

$app['security.access_rules'] = array(
  array('^/$', 'ROLE_ADMIN')
);

My User class:

<?php

namespace Coco\Model;

use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\EquatableInterface;

class User implements UserInterface, EquatableInterface
{
  private $username;
  private $password;
  private $roles;

  public function __construct($username, $password, array $roles)
  {
    $this->username = $username;
    $this->password = $password;
    $this->roles    = $roles;
  }

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

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

  public function getRoles()
  {
    return $this->roles;
  }

  public function eraseCredentials()
  {

  }

  public function getSalt()
  {
    return null;
  }

  public function isEqualTo(UserInterface $user)
  {
    if (false === $user instanceof User) {
      return false;
    } elseif ($this->password !== $user->getPassword()) {
      return false;
    } elseif ($this->username !== $user->getUsername()) {
      return false;
    } else {
      return true;
    }
  }
}

My UserProvider class:

<?php

namespace Coco\Provider;

use Coco\Exception\UnsupportedUserException;
use Coco\Exception\UsernameNotFoundException;
use Coco\Provider;
use Coco\Model\User;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;

class UserProvider extends Provider implements UserProviderInterface
{
  public function loadUserByUsername($username)
  {
    $username = strtolower($username);

    $query = "SELECT `username`, `password`, `roles` FROM `user` WHERE `status` = 'active' AND `username` = ?";
    $stmt  = $this->db->executeQuery($query, array($username));
    $user  = $stmt->fetch();

    if (false === $user) {
      throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username));
    }

    return new User(
      $user['username'],
      $user['password'],
      explode(',', $user['roles'])
    );
  }

  public function refreshUser(UserInterface $user)
  {
    if (false === $user instanceof User) {
      throw new UnsupportedUserException(sprintf('Instance of "%s" not supported.', get_class($user)));
    }

    return $this->loadUserByUsername($user->getUsername());
  }

  public function supportsClass($class)
  {
    return $class === 'Coco\Model\User';
  }
}

I have a SessionServiceProvider and a FormServiceProvider running.

I have no idea how to properly debug this issue because of a lack of familiarity with the framework. If anyone could point me in the right direction, it would be appreciated.

  • 写回答

1条回答 默认 最新

  • doufei2194 2015-07-02 11:46
    关注

    So, after some searching I found this GitHub issue report, which confirms that this is a HHVM/Symfony sessions issue which deviates from standard behaviour in PHP5.

    Switching to a PdoSessionHandler was a viable workaround for me, but will not work for all cases so although I'm marking this answer as 'right', it may not work for everyone.

    评论

报告相同问题?

悬赏问题

  • ¥15 想通过pywinauto自动电机应用程序按钮,但是找不到应用程序按钮信息
  • ¥15 MATLAB中streamslice问题
  • ¥15 如何在炒股软件中,爬到我想看的日k线
  • ¥15 51单片机中C语言怎么做到下面类似的功能的函数(相关搜索:c语言)
  • ¥15 seatunnel 怎么配置Elasticsearch
  • ¥15 PSCAD安装问题 ERROR: Visual Studio 2013, 2015, 2017 or 2019 is not found in the system.
  • ¥15 (标签-MATLAB|关键词-多址)
  • ¥15 关于#MATLAB#的问题,如何解决?(相关搜索:信噪比,系统容量)
  • ¥500 52810做蓝牙接受端
  • ¥15 基于PLC的三轴机械手程序