Symfony 的安全系统可以通过活动目录或开放授权服务器像数据库一样从任何地方加载安全用户。这篇文章将告诉你如何通过一个 Doctrine entity 从数据库加载用户信息。

前言

在开始之前,您应该检查 FOSUserBundle。这个外部包允许您从数据库加载用户信息(就像你会在这里学到的)并为您提供内置路径和控制器用于一些事务,比如登录、注册和忘记密码。但是,如果您需要大量自定义用户系统或者如果你想了解工作原理,本教程是更好的。

通过 Doctrine entity 加载用户信息有两个基本步骤:

  1. 创建您的用户对象;
  2. 把 security.yml 配置为从对象加载。

之后,您可以了解更多关于禁止非活动用户,使用自定义查询和序列化会话用户的相关信息。

1) 创建您的用户实体

此项,假设您在一个应用程序包内已经有一个用户对象包含下列字段:idusernamepasswordemailisActive

// src/AppBundle/Entity/User.php
namespace AppBundle\Entity;use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;/*** @ORM\Table(name="app_users")* @ORM\Entity(repositoryClass="AppBundle\Entity\UserRepository")*/
class User implements UserInterface, \Serializable
{/*** @ORM\Column(type="integer")* @ORM\Id* @ORM\GeneratedValue(strategy="AUTO")*/private $id;/*** @ORM\Column(type="string", length=25, unique=true)*/private $username;/*** @ORM\Column(type="string", length=64)*/private $password;/*** @ORM\Column(type="string", length=60, unique=true)*/private $email;/*** @ORM\Column(name="is_active", type="boolean")*/private $isActive;public function __construct(){$this->isActive = true;// may not be needed, see section on salt below// $this->salt = md5(uniqid(null, true));
    }public function getUsername(){return $this->username;}public function getSalt(){// you *may* need a real salt depending on your encoder// see section on salt belowreturn null;}public function getPassword(){return $this->password;}public function getRoles(){return array('ROLE_USER');}public function eraseCredentials(){}/** @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);}
}

为了让代码简短,一些 getter 和 setter 方法没有显示。但你可以通过运行生成这些:

$ php app/console doctrine:generate:entities AppBundle/Entity/User

接下来,确保创建数据库表:

$ php app/console doctrine:schema:update --force

什么是用户接口?

目前为止,这只是一个普通的对象。 但是为了在安全系统中使用此类,它必须实现用户接口。这要求该类有以下五种方法:

  • [getRoles()](http://api.symfony.com/2.7/Symfony/Component/Security/Core/User/UserInterface.html#getRoles())
  • [getPassword()](http://api.symfony.com/2.7/Symfony/Component/Security/Core/User/UserInterface.html#getPassword())
  • [getSalt()](http://api.symfony.com/2.7/Symfony/Component/Security/Core/User/UserInterface.html#getSalt())
  • [getUsername()](http://api.symfony.com/2.7/Symfony/Component/Security/Core/User/UserInterface.html#getUsername())
  • [eraseCredentials()](http://api.symfony.com/2.7/Symfony/Component/Security/Core/User/UserInterface.html#eraseCredentials())

要了解这五个方法的更多信息,请参见用户接口。

序列化和反序列化方法做什么?

在每个请求末,用户对象是序列化会话的。对下一个请求,它是非序列化的。要帮助 PHP 正确做到这一点,您需要实现可序列化。但你不必序列化任何东西:你只需要几个字段(上面所示的那些加上一些额外的字段,如果你决定实现 AdvancedUserInterface 的话)。对于每个请求,id 用于从数据库中查询一个新的用户对象。

想要了解更多吗?详见理解序列化和用户如何在会话中进行保存。

2) 从对象加载配置安全性

现在,您已经有了一个实现了用户接口用户对象,你只需要在 security.yml 中把这些告诉 Symfony 的安全系统。

在这个例子中,用户将通过 HTTP 基本身份验证输入用户名和密码。Symfony 会查询一个和用户名匹配的用户对象,然后检查密码(通常检查密码的用时较短):

YAML:

# app/config/security.yml
security:encoders:AppBundle\Entity\User:algorithm: bcrypt# ...
providers:our_db_provider:entity:class: AppBundle:Userproperty: username# if you're using multiple entity managers# manager_name: customer
firewalls:default:pattern:    ^/http_basic: ~provider: our_db_provider# ...

XML:

<!-- app/config/security.xml -->
<config><encoder class="AppBundle\Entity\User"algorithm="bcrypt"/><!-- ... --><provider name="our_db_provider"><entity class="AppBundle:User" property="username" /></provider><firewall name="default" pattern="^/" provider="our_db_provider"><http-basic /></firewall><!-- ... -->
</config>

PHP:

// app/config/security.php
$container->loadFromExtension('security', array('encoders' => array('AppBundle\Entity\User' => array('algorithm' => 'bcrypt',),),// ...'providers' => array('our_db_provider' => array('entity' => array('class'    => 'AppBundle:User','property' => 'username',),),),'firewalls' => array('default' => array('pattern' => '^/','http_basic' => null,'provider' => 'our_db_provider',),),// ...
));

首先,encoder 部分告诉 Symfony 期望使用 bcrypt 对数据库中的密码进行编码。第二,providers 部分创建一个叫 our_db_provider 的 "user provider",它知道通过 username 属性在您的 AppBundle:User 对象中查询。其中,our_db_provider 这个名字并不重要:它只需要在你的防火墙下匹配 provider 的密钥的值。或者,如果您没有在防火墙下设置 provider 密钥,第一个 “user provider” 会被自动使用。

If you're using PHP 5.4 or lower, you'll need to install the ircmaxell/password-compat library via Composer in order to be able to use the bcrypt encoder:如果您使用的是PHP 5.4 或更低版本,为了能够使用 bcrypt 编码器,您需要通过 Composer 安装 ircmaxell/password-compat 库:

If you're using PHP 5.4 or lower, you'll need to install the ircmaxell/password-compat library via Composer in order to be able to use the bcrypt encoder:如果您使用的是PHP 5.4 或更低版本,为了能够使用 bcrypt 编码器,您需要通过 Composer 安装 ircmaxell/password-compat 库:

创建您的第一个用户

要添加用户,您可以实现一个注册表单或添加一些 fixtures。这只是一个普通的对象,所以没什么棘手,除了您需要对每个用户的密码进行加密。但别担心,Symfony 会给您一个用来实现此事的服务。有关详细信息,请参见动态编码密码。

下面是从 MySQL 中导出的 app_users 表,包含了用户 admin 和密码 admin (密码是加密过的)。

$ mysql> SELECT * FROM app_users;
+----+----------+--------------------------------------------------------------+--------------------+-----------+
| id | username | password                                                     | email              | is_active |
+----+----------+--------------------------------------------------------------+--------------------+-----------+
|  1 | admin    | $2a$08$jHZj/wJfcVKlIwr5AvR78euJxYK7Ku5kURNhNx.7.CSIJ3Pq6LEPC | admin@example.com  |         1 |
+----+----------+--------------------------------------------------------------+--------------------+-----------+

您需要一个 salt 属性吗? 如果您使用 bcrypt,不需要。否则,是的。所有的密码必须用一个 salt 进行哈希处理,但是 bcrypt 内部做了这件事。由于本教程使用 bcryptUser 中的 getSalt() 方法只能返回空值(它没有被使用)。如果你使用了一个不同的算法,您需要在用户对象中取消对 salt 行的注释,并且添加一个持久的 salt 属性。

禁止非活动用户(AdvancedUserInterface)

如果用户 isActive 属性设置为 false (例如,is_active 在数据库中是 0),用户仍然可以正常登录到网站。这是很容易修正的。

排除非活动用户,更改您的用户类来实现 AdvancedUserInterface。这扩展了互动演示界面,所以你只需要新的界面:

// src/AppBundle/Entity/User.phpuse Symfony\Component\Security\Core\User\AdvancedUserInterface;
// ...class User implements AdvancedUserInterface, \Serializable
{// ...public function isAccountNonExpired(){return true;}public function isAccountNonLocked(){return true;}public function isCredentialsNonExpired(){return true;}public function isEnabled(){return $this->isActive;}// serialize and unserialize must be updated - see belowpublic function serialize(){return serialize(array(// ...$this->isActive));}public function unserialize($serialized){list (// ...$this->isActive) = unserialize($serialized);}
}

该 AdvancedUserInterface 接口添加四个额外的方法来验证帐户状态:

  • [isAccountNonExpired()](http://api.symfony.com/2.7/Symfony/Component/Security/Core/User/AdvancedUserInterface.html#isAccountNonExpired()) 检查用户帐户是否已过期;
  • [isAccountNonLocked()](http://api.symfony.com/2.7/Symfony/Component/Security/Core/User/AdvancedUserInterface.html#isAccountNonLocked()) 检查用户是否已被锁定;
  • [isCredentialsNonExpired()](http://api.symfony.com/2.7/Symfony/Component/Security/Core/User/AdvancedUserInterface.html#isCredentialsNonExpired()) 检查用户凭据是否(密码)已过期;
  • [isEnabled()](http://api.symfony.com/2.7/Symfony/Component/Security/Core/User/AdvancedUserInterface.html#isEnabled()) 检查用户是否已启用.

如果任何这些返回 false,则用户不会被允许登录。你可以选择坚持所有这些的属性,或任何你需要的(在这个例子中,isActive 是从数据库中选出的唯一属性)。

那么方法之间的区别是什么?每个方法返回一个略有不同的错误消息(当你在登录模板提交它们到自定义模式时,这些可以被转换)。

如果您使用 AdvancedUserInterface,您还需要添加任何由这些方法使用的属性(如 isActive)到 serialize()unserialize() 方法。如果你不这样做,您的用户可能无法从每个请求上的会话中正确反序列化。

恭喜您!您的数据库加载安全系统已完成所有设置!接下来,添加一个真正的登录表单代替 HTTP 基本身份验证或继续阅读其他主题。

使用自定义查询加载用户

如果用户可以用他们的用户名或电子邮件登录,这将是很好的,这在数据库中都是独特的。不幸的是,本机实体提供者只能通过单个用户的属性处理查询。

要做到这一点,使您的用户资料库执行一个特殊的 UserProviderInterface。此接口需要三个方法:loadUserByUsername($username)refreshUser(UserInterface $user)supportsClass($class)

// src/AppBundle/Entity/UserRepository.php
namespace AppBundle\Entity;use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Doctrine\ORM\EntityRepository;class UserRepository extends EntityRepository implements UserProviderInterface
{public function loadUserByUsername($username){$user = $this->createQueryBuilder('u')->where('u.username = :username OR u.email = :email')->setParameter('username', $username)->setParameter('email', $username)->getQuery()->getOneOrNullResult();if (null === $user) {$message = sprintf('Unable to find an active admin AppBundle:User object identified by "%s".',$username);throw new UsernameNotFoundException($message);}return $user;}public function refreshUser(UserInterface $user){$class = get_class($user);if (!$this->supportsClass($class)) {throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.',$class));}return $this->find($user->getId());}public function supportsClass($class){return $this->getEntityName() === $class|| is_subclass_of($class, $this->getEntityName());}
}

有关这些方法的详细信息,请参阅 UserProviderInterface。

别忘了将 repository 类添加到该实体的映射定义。

为了完成这一点,只需在 security.yml 中移除用户提供者的 property 键值。

YAML:

# app/config/security.yml
security:# ...providers:our_db_provider:entity:class: AppBundle:User# ...

XML:

<!-- app/config/security.xml -->
<config><!-- ... --><provider name="our_db_provider"><entity class="AppBundle:User" /></provider><!-- ... -->
</config>

PHP:

// app/config/security.php
$container->loadFromExtension('security', array(...,'providers' => array('our_db_provider' => array('entity' => array('class' => 'AppBundle:User',),),),...,
));

这告诉 Symfony 不要为用户自动查询。相反,当有人登录,用户资料库上的 loadUserByUsername() 方法将被调用。

了解序列化以及用户如何在会话中保存

如果你关心在用户类中 serialize() 方法的重要性或如何将用户对象序列化或反序列化,那么这一节适合于你。如果不是,可以跳过此节。

这告诉 Symfony 不要为用户自动查询。相反,当有人登录,用户资料库上的 loadUserByUsername() 方法将被调用。

了解序列化以及用户如何在会话中保存

如果你关心在用户类中 serialize() 方法的重要性或如何将用户对象序列化或反序列化,那么这一节适合于你。如果不是,可以跳过此节。

一旦用户登录,整个用户对象序列化到会话。对下一个请求,用户对象反序列化。然后,id 属性的值是用来从数据库中查询一个新的用户对象。最后,新的用户对象与反序列化的用户对象进行比较,以确保它们表示相同的用户。例如,如果由于某种原因,两个用户对象上的用户名不匹配,则出于安全原因,该用户将被注销。

尽管这一切会自动发生,但有一些重要的副作用。

首先,Serializable 接口和其序列化反序列化方法被添加到允许用户类序列化的会话。这可能是也可能不是根据您的设置完成的,但它可能是个好主意。从理论上讲,只有 id 需要被序列化,因为 [refreshUser()](http://api.symfony.com/2.7/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.html#refreshUser()) 方法在每个使用该 id 的请求上刷新用户(如上所述)。这给我们一个 "fresh" 用户对象。

但 Symfony 也使用用户名salt密码来验证用户请求之间没有改变(如果你执行它,它也会调用你的 AdvancedUserInterface 方法)。未能序列化这些会导致你在每个请求上被注销。如果您的用户实现 EquatableInterface,而不是检查这些属性,你的 isEqualTo 方法只是调用,那么您可以检查所需的任何属性。除非你理解这一点,您可能不需要实现该接口或担心这些。

更多翻译请查看(http://wiki.jikexueyuan.com/project/symfony-cookbook/)

转载于:https://www.cnblogs.com/linxingyun/p/5300865.html

如何从数据库(实体提供者)读取安全用户(转自http://wiki.jikexueyuan.com/project/symfony-cookbook/entity-provider.html)...相关推荐

  1. java环信聊天记录保存到数据库实体

    发现环信的根据时间条件拉取历史消息接口已经停用,就做了个通过导出聊天记录接口保存到数据库实体的功能,分享一下. 大致的思路: 1.通过环信的接口,把前一天24小时的数据压缩包下载到本地 2.把下载后的 ...

  2. python读取sqlserver数据库方法_SQLServer数据库之Python读取配置文件,并连接数据库SQL Server...

    本文主要向大家介绍了SQLServer数据库之Python读取配置文件,并连接数据库SQL Server,通过具体的内容向大家展现,希望对大家学习SQLServer数据库有所帮助. 用配置文件保存固定 ...

  3. json怎么读取数据库_如何:使用Json插入数据库并从中读取

    json怎么读取数据库 在本文中,我们将为Speedment创建一个插件,该插件使用Gson生成序列化和反序列化逻辑,从而使其在数据库实体和JSON字符串之间进行映射非常容易. 这将有助于展示Spee ...

  4. oracle权限培训,Java培训-ORACLE数据库学习【2】用户权限

    查询用户拥有的权限: 1.查看所有用户:select *from dba_users;select *from all_users;select *from user_users; 2.查看用户或角色 ...

  5. java+oracle数据库锁,数据库学习之Oracle数据库\记录被另一个用户锁住\解决方法...

    1.先来看看为什么会出锁住: 数据库是一个多用户使用的共享资源.当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况.若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数 ...

  6. fn_dblog_如何使用fn_dblog和fn_dump_dblog直接在SQL Server数据库中连续读取事务日志文件数据

    fn_dblog 大纲 (Outline) In this article, we'll discuss how to read SQL Server transaction logs. This a ...

  7. oracle sysobject,SQL2000数据库提示未能读取并闩锁页 sysobjects 失败数据库修复

    SQL2000数据库提示未能读取并闩锁页 sysobjects 失败数据库修复 哈尔滨海月数据恢复中心为哈尔滨某速达用户成功解决SQLServer2000数据库DBCC检测提示"未能读取并闩 ...

  8. 数据库实体联系模型与关系模型

    数据库设计是指根据用户的需求,在某一具体的数据库管理系统上,设计数据库的结构和建立数据库的过程.例如,编程微课是在线编程教育项目,该项目涉及到课程.学生.老师.学习资料等数据,这些数据都要被存储下来, ...

  9. 如何用Visio画数据库实体关系图

    在百度经验中浏览:http://jingyan.baidu.com/article/e4511cf3374a862b855eaf58.html 在设计数据库表结构时,通常都是先画数据库实体关系图,这样 ...

  10. MySQL 数据库 User表权限以及用户授权详解

    转载:https://blog.mimvp.com/article/27956.html MySQL 常用权限操作 1)本机登陆mysql: mysql -u root -p (-p一定要有):改变数 ...

最新文章

  1. 如何零基础或者转行数据分析师?
  2. [转]虚拟机网络模式简介
  3. mui ajax传参示例
  4. java中bitconverter_【Java】BitConverter(数字转字节数组工具类)
  5. 浏览器与node事件循环
  6. Android对话框
  7. Linux实战教学笔记01:计算机硬件组成与基本原理
  8. seqlist插入java_线性表插入c语言代码我想问的是seqlist *l中的*l不是太理解。它指向了哪里...
  9. 小米路由linux系统,Linux下玩转小米路由器文件访问.docx
  10. UR+RealSense手眼标定(eye-to-hand)
  11. 毫米焦距到像素焦距换算
  12. Java 开发中遇到的印象深刻的问题
  13. 关于打印机能够搜到但是无法连接的解决办法
  14. java计算机毕业设计线上教育教学系统源码+数据库+lw文档+系统+部署
  15. 案例:5秒后关闭广告 定时器
  16. PYTHON-小白入门-学习顺序
  17. springboot配置内置tomcat的日志
  18. STM32配置读取BMP280气压传感器数据
  19. Python综合小应用 —— 胖子老板香烟管理系统
  20. 计算机考研专业课课件,【考研计算机专业课】武汉大学计算机网络PPT课件 习题课.ppt...

热门文章

  1. NYOJ 972(蓝桥杯) 核桃的数量
  2. 概要设计的作用_【教育年会与颁奖典礼】第六届中国人居环境设计学年奖
  3. 翻译:使用 AWS Deep Racer 的日志分析工具
  4. 算法:平衡二叉树110. Balanced Binary Tree
  5. 极客大学架构师训练营 数据结构与算法 分布式数据库 Hadoop 第八次作业
  6. Amazon AWS创建Elastic Beanstalk,部署Tomcat,配置MySQL,发布Spring Boot应用
  7. spite用法 java_despite 和in spite of 用法
  8. 接口没获取到就被使用_使用CompletableFuture时,那些令人头疼的问题
  9. 随机过程第二章part2
  10. linux opendir路径_Linux C/C++ ----目录文件的操作(opendir,readdir,closedir)