使用控制器过滤器

在许多例子中,我们需要过滤输入的数据,或者基于这些数据执行一些动作。例如,使用自定义过滤器,我们可以使用IP过滤访问者,强制用户使用HTTPS,或者在使用应用之前,重定向用户到一个安装页面。

在Yii2中,过滤器本质上是一种特殊的behavior,所以使用过滤器和使用behavior是一样的。

Yii有许多内置的过滤器:

  • Core
  • Custom
  • Authentication
  • Content Negotiator
  • HttpCache
  • PageCache
  • RateLimiter
  • Verb
  • Cors

在本小节中,我们将实现如下内容:

  • 对控制器动作的访问限制到只有登录的用户
  • 对控制器动作的访问限制到指定的IP
  • 只允许指定用户角色访问

准备

  1. 按照官方指南http://www.yiiframework.com/doc-2.0/guide-start-installation.html的描述,使用Composer包管理器创建一个新的应用。
  2. 创建app/components/AccessRule.php
  1. <?php
  2. namespace app\components;
  3. use app\models\User;
  4. class AccessRule extends \yii\filters\AccessRule {
  5. /**
  6. * @inheritdoc
  7. */
  8. protected function matchRole($user)
  9. {
  10. if (empty($this->roles)) {
  11. return true;
  12. }
  13. $isGuest = $user->getIsGuest();
  14. foreach ($this->roles as $role) {
  15. switch($role) {
  16. case '?':
  17. return ($isGuest) ? true : false;
  18. case User::ROLE_USER:
  19. return (!$isGuest) ? true : false;
  20. case $user->identity->role: // Check if the user is logged in, and the roles match
  21. return (!$isGuest) ? true : false;
  22. default:
  23. return false;
  24. }
  25. }
  26. return false;
  27. }
  28. }
  1. 创建app/controllers/AccessController.php
  1. <?php
  2. namespace app\controllers;
  3. use app\models\User;
  4. use Yii;
  5. use yii\filters\AccessControl;
  6. use app\components\AccessRule;
  7. use yii\web\Controller;
  8. class AccessController extends Controller
  9. {
  10. public function behaviors()
  11. {
  12. return [
  13. 'access' => [
  14. 'class' => AccessControl::className(),
  15. // We will override the default rule config with the new AccessRule class
  16. 'ruleConfig' => [
  17. 'class' => AccessRule::className(),
  18. ],
  19. 'rules' => [
  20. [
  21. 'allow' => true,
  22. 'actions' => ['auth-only'],
  23. 'roles' => [User::ROLE_USER]
  24. ],
  25. [
  26. 'allow' => true,
  27. 'actions' => ['ip'],
  28. 'ips' => ['127.0.0.1'],
  29. ],
  30. [
  31. 'allow' => true,
  32. 'actions' => ['user'],
  33. 'roles' => [ User::ROLE_ADMIN],
  34. ],
  35. [
  36. 'allow' => false,
  37. ]
  38. ],
  39. ]
  40. ];
  41. }
  42. public function actionAuthOnly()
  43. {
  44. echo "Looks like you are authorized to run me.";
  45. }
  46. public function actionIp()
  47. {
  48. echo "Your IP is in our list. Lucky you!";
  49. }
  50. public function actionUser()
  51. {
  52. echo "You're the right man. Welcome!";
  53. }
  54. }
  1. 修改User类:
  1. <?php
  2. namespace app\models;
  3. class User extends \yii\base\Object implements \yii\web\IdentityInterface
  4. {
  5. // add roles contstants
  6. CONST ROLE_USER = 200;
  7. CONST ROLE_ADMIN = 100;
  8. public $id;
  9. public $username;
  10. public $password;
  11. public $authKey;
  12. public $accessToken;
  13. public $role;
  14. private static $users = [
  15. '100' => [
  16. 'id' => '100',
  17. 'username' => 'admin',
  18. 'password' => 'admin',
  19. 'authKey' => 'test100key',
  20. 'accessToken' => '100-token',
  21. 'role' => USER::ROLE_ADMIN // add admin role for admin user
  22. ],
  23. '101' => [
  24. 'id' => '101',
  25. 'username' => 'demo',
  26. 'password' => 'demo',
  27. 'authKey' => 'test101key',
  28. 'accessToken' => '101-token',
  29. 'role' => USER::ROLE_USER // add user role for admin user
  30. ],
  31. ];
  32. //…
  33. }

如何做…

  1. 为了使用AccessControl,在你的控制器的behaviors()方法中声明:
  1. public function behaviors()
  2. {
  3. return [
  4. 'access' => [
  5. 'class' => AccessControl::className(),
  6. 'rules' => [
  7. [
  8. 'allow' => true,
  9. 'actions' => ['auth-only'],
  10. 'roles' => ['@'],
  11. ],
  12. [
  13. 'allow' => true,
  14. 'actions' => ['ip'],
  15. 'ips' => ['127.0.0.1'],
  16. ],
  17. [
  18. 'allow' => true,
  19. 'actions' => ['user'],
  20. 'roles' => ['admin'],
  21. ],
  22. [
  23. 'allow' => true,
  24. 'actions' => ['user'],
  25. 'matchCallback' => function ($rule, $action) {
  26. return preg_match('/MSIE9/',$_SERVER['HTTP_USER_AGENT']) !== false;
  27. }
  28. ],
  29. ['allow' => false]
  30. ],
  31. ]
  32. ];
  33. }
  1. 尝试使用IE浏览器和其他浏览器运行控制器动作,使用admindemo用户。

工作原理…

我们开始限制控制器动作给已经登录的用户,查看如下rules数组中的代码:

  1. [
  2. 'allow' => true,
  3. 'actions' => ['auth-only'],
  4. 'roles' => [User::ROLE_USER]
  5. ],

每一个数组都是一个访问规则。你可以使用allow=true或者allow=>false。对于每一个规则,有若干个参数。

缺省情况下,Yii不会拒绝任何事情,所以如果你需要最大程度的安全,考虑添加['allow' => false]到规则的末尾。

在我们的规则中,我们使用了两个参数。第一个是动作参数,它指定的该规则会应用在哪些动作上。第二个参数时角色参数,它指定了该规则会应用于哪些角色上。

Yii2内置访问控制默认只支持两个角色:游客(未登录),用符号?指定,登录的用户,用服务@指定。

使用简单的访问控制,我们可以基于用户的登录状态限制对指定页面的访问。如果用户在未登录状态下访问这些页面,Yii会将他们重定向到登录页面。

规则会一个接一个执行,从第一个开始,直到能匹配上一个。如果没有一个能匹配,那么该访问被认为是允许的。

下一个任务是限制指定IP的访问。在这个例子中,涉及如下两个访问规则:

  1. [
  2. 'allow' => true,
  3. 'actions' => ['ip'],
  4. 'ips' => ['127.0.0.1'],
  5. ],

第一个规则允许指定IP列表中的IP访问。在我们的例子中,我们使用了一个回路地址,它指向我们自己的电脑。尝试将其修改为127.0.0.2,看看当IP地址不匹配时是什么表现。第二个规则是拒绝所以,包括其它所有IP。

接下来,我们只允许指定用户角色访问:

  1. [
  2. 'allow' => true,
  3. 'actions' => ['user'],
  4. 'roles' => [ User::ROLE_ADMIN],
  5. ],

上边的规则允许admin角色的用户访问user动作,因此,如果你以admin登录,你将可以访问,但如果你以demo登录,将会被拒绝。

使用控制器过滤器 - 图1

我们重写了标准AccessRule类,存放在components/AccessRule.php。在我们的AccessRule类内部,我们重写了matchRole方法,在这我们获取并检查了当前用户的角色,并使用我们的规则进行匹配。

最后,我们需要拒绝指定浏览器的访问。在本小节中,我们只拒绝IE 9的访问。这个规则被放在了首位,所以它首先会执行:

  1. [
  2. 'all`ow' => true,
  3. 'actions' => ['user'],
  4. 'matchCallback' => function ($rule, $action) {
  5. return preg_match('/MSIE 9/',$_SERVER['HTTP_USER_AGENT'])!==false;
  6. }`
  7. ],

我们使用的这个检测技术是不可靠的,因为MSIE在其它很多用户代理中都会包含。想了解用户代理字符串的列表,可以参考http://www.useragentstring.com/

在上边的代码中,我们使用了另外一个过滤规则属性,名叫matchCallback。这个属性表明只有当函数中的属性返回true时规则会生效。

我们的函数检查用户代理字符串是否包含MSIE 9.0字符串。你可以根据自己的需求,指定任何PHP代码。

参考

为了了解更多有关访问控制和过滤器的信息,参考如下地址: