在现在的开发中,大多数的应用是采用前后端分离的方式进行开发的,这样可以灵活的适应多种客户端。但是,问题也来了,安全在这个时候就很重要了。当然,yii已经给我们提供了现成的认证授权,使用起来很方便。但是,有时候也不是完全能够满足我们的实际需求,这个时候就需要我们自己来重写来修改了。前提是,我们要对这个框架有足够的了解。
好了,废话不多说,带你一起来解读下,先来了解了解认证授权模块的代码结构(如下图所示),先来说说图中红色箭头指出的三者之前的关系,基础的权限抽象类(AuthMethod.php)继承了定义权限功能接口(AuthInetrface.php)和动作过滤器类(ActionFilter):
(1)定义权限功能接口(AuthInetrface.php),里面定义了三个功能。分别是:
1、用户认证;
2、挑战(这个得特别说明一下,用基础认证来方式来说吧,客户端请求一个接口,然后服务端接收到请求后,会检查请求发送来的数据中是否包含有: “Authorization: Basic YWRtaW46YWRtaW4=”这种格式的数据,若无,则服务端会发送HTTP信息头“WWW-Authenticate: Basic realm=“.api””,这个时候浏览器会弹出一个要求你输入用户名密码的弹出框,输入完用户名密码点击发送至服务端,这个时候服务端会解析用户名密码,若合法,则会返回你想要请求的信息。反之,会继续弹框,让你输入用户名密码);
3、处理异常(直接抛出一个401 无权限提示)。
(2)权限方法类(AuthMethod.php)
这个类里面主要重写了动作执行前过滤。下图中有个方法需要特别注意下:
上图箭头指出的方法,主要实现了用户认证功能,该里面有一个重要的方法实现,用基础权限认证来举个例子。
看下面红色方框中的方法,用木有很熟悉,对,他就是你用自己的方式认证用户重写的方法,记得在认证组件中配置奥。
(3)上面说了那么多,下面来说说怎样修改成满足你自己需求的过滤器。
<?php
namespace app\behaviors;
use Yii;
use yii\base\InvalidConfigException;
use yii\filters\auth\AuthInterface;
use yii\filters\auth\AuthMethod;
use yii\helpers\StringHelper;
use yii\web\ForbiddenHttpException;
use yii\web\UnauthorizedHttpException;
/**
* 权限校验
* Class CheckPermission
* @package app\behaviors
*/
class CompositeAuth extends AuthMethod
{
public $authMethods = [];
/**
* @var array 权限排除列表,排除exclude中的权限,不去做权限验证
*/
public $exclude = [];
public function beforeAction($action) : bool
{
if (empty($this->authMethods)) {
return true;
}
$response = $this->response ?: Yii::$app->getResponse();
try {
$identity = $this->authenticate(
$this->user ?: Yii::$app->getUser(),
$this->request ?: Yii::$app->getRequest(),
$response
);
//用户必须要求登录,且已登录,且需要做权限验证
if ($identity !== null && !$this->isExclude($action)) {
$rights = $identity->normal_right ?? [];
$uniqueId = $action->getUniqueId();
if (null !== $identity && !isset($rights[$uniqueId])) {
throw new ForbiddenHttpException('您当前请求没有权限,请联系管理员');
}
}
} catch (UnauthorizedHttpException $e) {
if ($this->isOptional($action)) {
return true;
}
throw $e;
}
if ($identity !== null || $this->isOptional($action)) {
return true;
}
$this->challenge($response);
$this->handleFailure($response);
return false;
}
/**
* {@inheritdoc}
*/
public function authenticate($user, $request, $response)
{
foreach ($this->authMethods as $i => $auth) {
if (!$auth instanceof AuthInterface) {
$this->authMethods[$i] = $auth = Yii::createObject($auth);
if (!$auth instanceof AuthInterface) {
throw new InvalidConfigException(get_class($auth) . ' must implement yii\filters\auth\AuthInterface');
}
}
$identity = $auth->authenticate($user, $request, $response);
if ($identity !== null) {
return $identity;
}
}
return null;
}
/**
* {@inheritdoc}
*/
public function challenge($response)
{
foreach ($this->authMethods as $method) {
/* @var $method AuthInterface */
$method->challenge($response);
}
}
protected function isExclude($action)
{
$id = $this->getActionId($action);
foreach ($this->exclude as $pattern) {
if (StringHelper::matchWildcard($pattern, $id)) {
return true;
}
}
return false;
}
}
(4)配置你自定义的、满足你需求的认证授权方式。
<?php
namespace app\controllers;
use yii\rest\ActiveController;
use Yii;
use yii\behaviors\CompositeAuth; //重写认证授权实现方式
use yii\filters\auth\HttpBasicAuth;
use yii\filters\auth\HttpBearerAuth;
use yii\filters\auth\QueryParamAuth;
class ArticleController extends ActiveController
{
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['authenticator'] = [
'class' => CompositeAuth::class,
'authMethods' => [
HttpBasicAuth::class,
HttpBearerAuth::class,
],
'optional' => [ //optional,框架已定义的不需要授权的路由数组
'index',
],
'except' => [ //except 自定义参数,认证但无需授权的路由数组
'about-create'
],
];
return $behaviors;
}
}