| | | |~filters/
| | | | |~auth/
| | | | | |-AuthInterface.php
| | | | | |-AuthMethod.php
| | | | | |-CompositeAuth.php
| | | | | |-HttpBasicAuth.php
| | | | | |-HttpBearerAuth.php
| | | | | `-QueryParamAuth.php
一、HttpBearer
驗證方式:
<?php
namespace app\controllers;
use yii\rest\ActiveController;
use yii\filters\auth\HttpBasicAuth;
use yii\helpers\ArrayHelper;
#use yii\filters\auth\CompositeAuth;
#use yii\filters\auth\HttpBasciAuth;
use yii\filters\auth\HttpBearerAuth;
#use yii\filters\auth\QueryParamAuth;
class UsersController extends ActiveController
{
public $modelClass = 'app\models\User2';
public function behaviors()
{
return ArrayHelper::merge(parent::behaviors(), [
'authenticator' => [
'class' => HttpBearerAuth::className(),
#這個地方使用`ComopositeAuth` 混合認證
#'class' => CompositeAuth::className(),
#`authMethods` 中的每一個元素都應該是 一種 認證方式的類或者一個 配置數組
//'authMethods' => [
//HttpBasicAuth::className(),
//HttpBearerAuth::className(),
//QueryParamAuth::className(),
//]
]
]);
}
下面來分析相關的源碼,看看 yii2/rest 是如何處理權限驗證的。
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\filters\auth;
use Yii;
use yii\web\UnauthorizedHttpException;
/**
* HttpBearerAuth is an action filter that supports the authentication method based on HTTP Bearer token.
*
* You may use HttpBearerAuth by attaching it as a behavior to a controller or module, like the following:
*
* public function behaviors()
* {
* return [
* 'bearerAuth' => [
* 'class' => \yii\filters\auth\HttpBearerAuth::className(),
* ],
* ];
* }
*
* @author Qiang Xue <[email protected]>
* @since 2.0
*/
class HttpBearerAuth extends AuthMethod
{
/**
* @var string the HTTP authentication realm
*/
public $realm = 'api';
/**
* @inheritdoc
*/
public function authenticate($user, $request, $response)
{
$authHeader = $request->getHeaders()->get('Authorization');
if ($authHeader !== null && preg_match("/^Bearer\\s+(.*?)$/", $authHeader, $matches)) {
$identity = $user->loginByAccessToken($matches[1], get_class($this));
if ($identity === null) {
$this->handleFailure($response);
}
return $identity;
}
return null;
}
/**
* @inheritdoc
*/
public function handleFailure($response)
{
$response->getHeaders()->set('WWW-Authenticate', "Bearer realm=\"{$this->realm}\"");
throw new UnauthorizedHttpException('You are requesting with an invalid access token.');
}
}
要想順利通過此驗證,需要在 Header
中加入一項:Authorization:Bearer ganiks-token
如果沒有如上提供正確的access-token
, 則會得到一個:
401 Unauthorized
Www-Authenticate:Bearer realm="api"
二、HttpBasic
驗證方式:
class HttpBasicAuth extends AuthMethod
{
/**
* @var string the HTTP authentication realm
*/
public $realm = 'api';
/**
* @var callable a PHP callable that will authenticate the user with the HTTP basic auth information.
* The callable receives a username and a password as its parameters. It should return an identity object
* that matches the username and password. Null should be returned if there is no such identity.
*
* The following code is a typical implementation of this callable:
*
* function ($username, $password) {
* return \app\models\User::findOne([
* 'username' => $username,
* 'password' => $password,
* ]);
* }
*
* If this property is not set, the username information will be considered as an access token
* while the password information will be ignored. The [[\yii\web\User::loginByAccessToken()]]
* method will be called to authenticate and login the user.
*/
public $auth;
/**
* @inheritdoc
*/
public function authenticate($user, $request, $response)
{
$username = $request->getAuthUser();
$password = $request->getAuthPassword();
if ($this->auth) {
if ($username !== null || $password !== null) {
$identity = call_user_func($this->auth, $username, $password);
if ($identity !== null) {
$user->switchIdentity($identity);
} else {
$this->handleFailure($response);
}
return $identity;
}
} elseif ($username !== null) {
$identity = $user->loginByAccessToken($username, get_class($this));
if ($identity === null) {
$this->handleFailure($response);
}
return $identity;
}
return null;
}
/**
* @inheritdoc
*/
public function handleFailure($response)
{
$response->getHeaders()->set('WWW-Authenticate', "Basic realm=\"{$this->realm}\"");
throw new UnauthorizedHttpException('You are requesting with an invalid access token.');
}
}
如果要順利通過此驗證,有2種方式:
access-token
作爲Basci Auth
的username
一起請求,至於password
則不必管- 定義下
use yii\filters\auth\HttpBasciAuth
的$auth
爲一個驗證的方法,來根據請求中的username:password
來返回一個identity
默認的, $auth
是沒有定義的,此時,在Header中加入:Authorization:Basic Z2FuaWtzLXRva2VuOg==
這個碼是由username: ganiks-token
經過base64編碼得到的,可以得到正確的響應
反而是用username:password
(正確的)也無法得到響應
第二種方式,
可以配置 use yii\filters\auth\HttpBasciAuth
的$auth
爲一個
callable
如下:
public $auth;
public function auth ($username, $password) {
return \app\models\User2::findOne([
'username' => $username,
'password' => $password,
]);
}
下面是一個臨時的解決方案,使用 username:password_hash
驗證
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\filters\auth;
use Yii;
use yii\web\UnauthorizedHttpException;
class HttpBasicAuth extends AuthMethod
{
public $auth;
public function auth ($username, $password) {
return \app\models\User2::findOne([
'username' => $username,
'password_hash' => $password,
]);
}
/**
* @inheritdoc
*/
public function authenticate($user, $request, $response)
{
$username = $request->getAuthUser();
$password = $request->getAuthPassword();
if ($this->auth($username, $password)) {
if ($username !== null || $password !== null) {
//$identity = call_user_func($this->auth, $username, $password);
$identity = $this->auth($username, $password);
if ($identity !== null) {
$user->switchIdentity($identity);
} else {
$this->handleFailure($response);
}
return $identity;
}
} elseif ($username !== null) {
$identity = $user->loginByAccessToken($username, get_class($this));
if ($identity === null) {
$this->handleFailure($response);
}
return $identity;
}
return null;
}
/**
* @inheritdoc
*/
public function handleFailure($response)
{
$response->getHeaders()->set('WWW-Authenticate', "Basic realm=\"{$this->realm}\"");
throw new UnauthorizedHttpException('You are requesting with an invalid access token.');
}
}
三、QueryParam
驗證方式:
此方式最簡單,只需要在請求的URL後面加上?access-toekn=ganiks-token
即可驗證
class QueryParamAuth extends AuthMethod
{
/**
* @var string the parameter name for passing the access token
*/
public $tokenParam = 'access-token';
/**
* @inheritdoc
*/
public function authenticate($user, $request, $response)
{
$accessToken = $request->get($this->tokenParam);
if (is_string($accessToken)) {
$identity = $user->loginByAccessToken($accessToken, get_class($this));
if ($identity !== null) {
return $identity;
}
}
if ($accessToken !== null) {
$this->handleFailure($response);
}
return null;
}
/**
* @inheritdoc
*/
public function handleFailure($response)
{
throw new UnauthorizedHttpException(Yii::t('yii', 'You are requesting with an invalid access token.'));
}
四、compositeAuth
綜合驗證方式:
class CompositeAuth extends AuthMethod
{
/**
* @var array the supported authentication methods. This property should take a list of supported
* authentication methods, each represented by an authentication class or configuration.
*
* If this property is empty, no authentication will be performed.
*
* Note that an auth method class must implement the [[\yii\filters\auth\AuthInterface]] interface.
*/
public $authMethods = [];
/**
* @inheritdoc
*/
public function beforeAction($action)
{
return empty($this->authMethods) ? true : parent::beforeAction($action);
}
/**
* @inheritdoc
*/
public function authenticate($user, $request, $response)
{
foreach ($this->authMethods as $i => $auth) {
$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;
}
}
if (!empty($this->authMethods)) {
/* @var $auth AuthInterface */
$auth = reset($this->authMethods);
$auth->handleFailure($response);
}
return null;
}