我是第一次使用Symfony2开发一个支持Doctrine2的小项目。目前我正在努力使用symfony2的安全组件,与documentation中描述的身份验证机制完全相同。
我想使用基于表单的身份验证并完成文档中所述的所有内容:
我有一个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将从“无安全”区域中排除。
在我的路由配置中,我包含两个身份验证规则:
_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支持的),这使得学习过程有点困难。我对身份验证如何工作的想法是,有一些神奇的Controller捕获validateLogin操作,为我的User实体查找实体存储库,调用findOneBy('username'=&gt; $ username)并比较密码...是这对吗?
提前感谢您的任何提示,我已经在Google上搜索了这个问题了几个小时...... :)
保
答案 0 :(得分:20)
我对身份验证如何工作的想法是,有一些神奇的Controller捕获validateLogin操作,为我的User实体查找实体存储库,调用findOneBy('username'=&gt; $ username)并比较密码。 ..这是对的吗?
你错了。身份验证不涉及任何控制器,这就是您不在_security_check
路由中指定任何控制器的原因。 Auth基于EventDispatcher
。每当您在防火墙中指定一些侦听器时(例如form_login
,anonymous
,logout
等),您实际上会为core.security
事件注册一个新的侦听器。 Symfony\Component\HttpKernel\Security\Firewall::handle()
是这些听众实际注册的地方。
一般简化流程:
_username
和_password
字段)。core.security
事件被解雇。UsernamePasswordFormAuthenticationListener
被解雇(handle()
方法)并检查是否:
check_path
选项。_username
和_password
个参数。attemptAuthentication()
方法)进行身份验证。DaoAuthenticationProvider
被触发,它尝试使用Doctrine的用户存储库类检索用户。UsernamePasswordToken
(包含$user
方法返回的loadUserByUsername()
对象)将被返回并且用户被重定向。确实,安全机制非常复杂且难以理解(文档尚未完成)。但是当你终于明白它是如何工作的时候,你会看到它的机制是多么强大。
我编写了自己的身份验证机制,它运行正常。
配置:
我正在使用自定义提供程序和编码器。
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/*
受到限制,而/*
是公开的。
服务security.encoder.sha512
是内置编码器:
<service id="security.encoder.sha512" class="%security.encoder.digest.class%">
<argument>sha512</argument>
</service>
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.
}
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());
}
}
安全路线和控制器与您的相同。
答案 1 :(得分:4)
您应该使用https://github.com/FriendsOfSymfony/FOSUserBundle FOS UserBundle,它使用Doctrine 2实现所有这些功能并具有大量功能。
答案 2 :(得分:1)
基本上,为什么登录页面再次加载而没有错误消息的原因是,具有讽刺意味的是,您的安全设置未设置为允许匿名访问登录页面。