Browse Source

Merge branch 'message_v1' of https://dev.33.cn/yanglin/logSystem-advance into message_v1

yanglin 6 years ago
parent
commit
6248403ae1

+ 3 - 0
backend/config/main.php

@@ -16,6 +16,9 @@ return [
         'request' => [
 			'baseUrl' => '',
 			'enableCsrfValidation' => false,
+			'parsers' => [
+				'application/json' => 'yii\web\JsonParser'
+			]
         ],
         'user' => [
             'identityClass' => 'common\models\User',

+ 49 - 0
backend/controllers/AuthController.php

@@ -0,0 +1,49 @@
+<?php
+namespace backend\controllers;
+
+use backend\forms\AuthForm;
+use yii\helpers\ArrayHelper;
+use Yii;
+
+class AuthController extends BaseController
+{
+	/**
+	 * behaviors
+	 * @return array
+	 */
+	public function behaviors()
+	{
+		return ArrayHelper::merge(parent::behaviors(), [
+			'verbs' => [
+				'class' => \yii\filters\VerbFilter::className(),
+				'actions' => [
+					'token' => ['POST'],
+				],
+			],
+		]);
+	}
+
+	/**
+	 * 获取accessToken
+	 * @author: libingke
+	 */
+	public function actionToken()
+	{
+		$model = new AuthForm();
+		$model->setScenario('access_token');
+		$model->load(['AuthForm' => Yii::$app->request->post()]);
+
+		$data = [];
+		if ($model->validate()) {
+			$data = $model->getAccessToken();
+		} else {
+			$model->handleError();
+		}
+
+		return [
+			'code' => 200,
+			'message' => Yii::t('error', 200),
+			'data' => $data
+		];
+	}
+}

+ 20 - 1
backend/controllers/BaseController.php

@@ -2,6 +2,7 @@
 namespace backend\controllers;
 
 use components\Exception;
+use yii\helpers\ArrayHelper;
 use yii\web\Controller;
 
 /**
@@ -19,6 +20,24 @@ class BaseController extends Controller
 		parent::init();
 	}
 
+	/**
+	 * behaviors
+	 * @return array
+	 */
+	public function behaviors()
+	{
+		return ArrayHelper::merge(parent::behaviors(), [
+			'bearerAuth' => [
+				'class' => \yii\filters\auth\HttpBearerAuth::className(),
+				'optional' => [
+					'token',
+					'signature',
+					'test',
+				],
+			]
+		] );
+	}
+
 	/**
 	 * beforeAction
 	 * @return bool
@@ -30,7 +49,7 @@ class BaseController extends Controller
 			return parent::beforeAction($action);
 
 		} catch (\Exception $e) {
-			throw new Exception($e->statusCode, $e->getMessage());
+			throw new Exception(isset($e->statusCode) ? $e->statusCode : $e->getCode(), $e->getMessage());
 		}
 	}
 }

+ 4 - 3
backend/controllers/MessageController.php

@@ -1,8 +1,9 @@
 <?php
 namespace backend\controllers;
 
-use backend\forms\MessageForm;
 use components\Exception;
+use backend\forms\MessageForm;
+use yii\helpers\ArrayHelper;
 use Yii;
 
 class MessageController extends BaseController
@@ -13,7 +14,7 @@ class MessageController extends BaseController
 	 */
 	public function behaviors()
 	{
-		return [
+		return ArrayHelper::merge(parent::behaviors(), [
 			'verbs' => [
 				'class' => \yii\filters\VerbFilter::className(),
 				'actions' => [
@@ -26,7 +27,7 @@ class MessageController extends BaseController
 					'ack' => ['POST'],
 				],
 			],
-		];
+		]);
 	}
 
 	/**

+ 3 - 2
backend/controllers/QueryController.php

@@ -4,6 +4,7 @@ namespace backend\controllers;
 use components\Exception;
 use components\service\AmqpConfig;
 use components\service\Redis;
+use yii\helpers\ArrayHelper;
 use Yii;
 
 class QueryController extends BaseController
@@ -14,14 +15,14 @@ class QueryController extends BaseController
 	 */
 	public function behaviors()
 	{
-		return [
+		return ArrayHelper::merge(parent::behaviors(), [
 			'verbs' => [
 				'class' => \yii\filters\VerbFilter::className(),
 				'actions' => [
 					'message-status' => ['GET'],
 				],
 			],
-		];
+		]);
 	}
 
 	/**

+ 3 - 2
backend/controllers/QueueController.php

@@ -3,6 +3,7 @@ namespace backend\controllers;
 
 use backend\forms\QueueForm;
 use components\Exception;
+use yii\helpers\ArrayHelper;
 use Yii;
 
 class QueueController extends BaseController
@@ -13,7 +14,7 @@ class QueueController extends BaseController
 	 */
 	public function behaviors()
 	{
-		return [
+		return ArrayHelper::merge(parent::behaviors(), [
 			'verbs' => [
 				'class' => \yii\filters\VerbFilter::className(),
 				'actions' => [
@@ -22,7 +23,7 @@ class QueueController extends BaseController
 					'delete' => ['POST'],
 				],
 			],
-		];
+		]);
 	}
 
 	/**

+ 120 - 0
backend/forms/AuthForm.php

@@ -0,0 +1,120 @@
+<?php
+
+namespace backend\forms;
+
+use common\models\AccessToken;
+use common\models\Client;
+use common\models\User;
+use components\Exception;
+use Yii;
+
+class AuthForm extends BaseForm
+{
+	/**
+	 * @var
+	 */
+	public $username;
+
+	/**
+	 * @var
+	 */
+	public $password;
+
+	/**
+	 * @var null
+	 */
+	public $access_token = null;
+
+	/**
+	 * @var null
+	 */
+	private $_user = null;
+
+	/**
+	 * @var
+	 */
+	private $_response;
+
+	/**
+	 * 失效时长
+	 */
+	const EXPIRES = 30 * 86400;
+
+
+	public function rules()
+	{
+		return [
+			[['username', 'password'], 'required', 'on' => ['access_token']],
+			[['username', 'password'], 'trim', 'on' => ['access_token']],
+			//['username', 'validateUser', 'on' => 'access_token'],
+			['password', 'validatePassword', 'on' => ['access_token']],
+		];
+	}
+
+	public function validatePassword($attribute)
+	{
+		if (!$this->hasErrors()) {
+			if (!$this->findUser(['username' => $this->username, 'status' => User::STATUS_ACTIVE])
+				|| !$this->password
+				|| !$this->_user['password']
+				|| !Yii::$app->security->validatePassword($this->password, $this->_user['password']))
+			{
+				$this->addError($attribute, 2001);
+			}
+		}
+	}
+
+	/**
+	 * getAccessToken
+	 * @author: libingke
+	 * @return string
+	 */
+	public function getAccessToken()
+	{
+		$this->generateAccessToken();
+		return $this->_response;
+	}
+
+	public function generateAccessToken()
+	{
+		$this->access_token = Yii::$app->security->generateRandomString();
+
+		$expires = strtotime(date('Y-m-d 23:59:59')) + static::EXPIRES;
+		//insert
+		if ( !($one = AccessToken::findOne(['access_token' => $this->access_token])) ) {
+			$model = new AccessToken();
+			$model->access_token	= $this->access_token;
+			$model->user_id 		= $this->_user['uid'];
+			$model->ip				= isset(Yii::$app->request->userIP) ? Yii::$app->request->userIP : '';
+			$model->user_agent		= isset(Yii::$app->request->userAgent) ? Yii::$app->request->userAgent : '';
+			$model->expires			= $expires;
+			if (!$model->save(false))
+				throw new Exception(2002);
+		}
+
+		$this->_response = array(
+			"access_token" => $this->access_token,
+			"expires" => $expires
+		);
+	}
+
+	/**
+	 * findUser
+	 * @author: libingke
+	 * @param array $query
+	 */
+	public function findUser(Array $query = [])
+	{
+		if (!$this->_user) {
+			$user = User::findOne($query);
+			if ($user)
+				$this->_user = [
+					'uid' => $user->id,
+					'username' => $user->username,
+					'password' => $user->password_hash
+				];
+		}
+
+		return $this->_user;
+	}
+}

+ 8 - 3
backend/forms/MessageForm.php

@@ -408,7 +408,7 @@ class MessageForm extends BaseForm
 
 		try {
 			$callback = function ($msg) use($q_name) {
-				call_user_func_array([$this, 'closureAck'], [$msg, $q_name, AmqpConfig::STATUS_HAND_OK]);
+				call_user_func_array([$this, 'closureAck'], [$msg, $q_name, AmqpConfig::STATUS_HAND_OK, true]);
 			};
 			$channel->basic_qos(0, $total, null);
 			$channel->basic_consume($q_name,
@@ -487,19 +487,24 @@ class MessageForm extends BaseForm
 	 * closureAck for ackMessage && deleteMessage
 	 * @author: libingke
 	 * @param $msg
-	 * @param $queue
+	 * @param string $queue
 	 * @param bool $status
+	 * @param bool $check
 	 */
-	protected function closureAck($msg, $queue = '', $status = false)
+	protected function closureAck($msg, $queue = '', $status = false, $check = false)
 	{
 		try {
 			$mid = $msg->get('message_id');
 			if (in_array($mid, $this->_rows)) {
+				if ($check == true && Redis::get($queue, $mid, 'status') != AmqpConfig::STATUS_HAND) {
+					goto end;
+				}
 				unset($this->_rows[$mid]);
 				$msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
 				if ($status != false && $queue != '')
 					Redis::set($queue, $mid, 'status', $status);
 			}
+			end:
 
 		} finally {
 			if (count($this->_rows) == 0)

+ 45 - 0
common/filters/auth/BearerAuth.php

@@ -0,0 +1,45 @@
+<?php
+namespace common\filters\auth;
+
+use yii\filters\auth\AuthMethod;
+
+/**
+ * Class BearerAuth
+ * @package common\filters\auth
+ */
+class BearerAuth extends AuthMethod
+{
+	public $header = 'FzmMQ';
+
+	/**
+	 * @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('/^' . $this->header . '\s+(.*?):(.*?)$/', $authHeader, $matches)) {
+			$identity = $user->loginByAccessToken($matches[1].':'.$matches[2], get_class($this));
+			if ($identity === null) {
+				$this->handleFailure($response);
+			}
+
+			return $identity;
+		}
+
+		return null;
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function challenge($response)
+	{
+		$response->getHeaders()->set('WWW-Authenticate', "Bearer realm=\"{$this->realm}\"");
+	}
+}

+ 6 - 0
common/messages/zh-CN/errorCode.php

@@ -111,4 +111,10 @@ return [
 	'1304' => '消费回调错误',
 
 	//ack 1400~1499
+
+	/**
+	 * 其它模块
+	 */
+	'2001' => '身份验证失败',
+	'2002' => '获取token失败',
 ];

+ 44 - 0
common/models/AccessToken.php

@@ -0,0 +1,44 @@
+<?php
+namespace common\models;
+
+use Yii;
+use yii\behaviors\TimestampBehavior;
+use yii\db\ActiveRecord;
+
+/**
+ * Class AccessToken
+ * @package common\models
+ */
+class AccessToken extends ActiveRecord
+{
+	/**
+	 * @inheritdoc
+	 */
+	public function behaviors()
+	{
+		return [
+			[
+				'class' => TimestampBehavior::className(),
+				'createdAtAttribute' => 'created_at',
+				'updatedAtAttribute' => 'updated_at',
+				//'value'   => new Expression('NOW()'),
+				'value'   => function() { return time();},
+			],
+		];
+	}
+
+	/**
+	 * getUidByApiToken
+	 * @param $token
+	 * @return bool
+	 */
+	public static function getUidByApiToken($token)
+	{
+		$uid = static::find()->select('user_id')
+			->where(['access_token' => $token])
+			->andWhere(['>', 'expires', time()])
+			->scalar();
+
+		return $uid != null && is_numeric($uid) ? $uid : null;
+	}
+}

+ 32 - 0
common/models/Client.php

@@ -0,0 +1,32 @@
+<?php
+namespace common\models;
+
+use Yii;
+use yii\behaviors\TimestampBehavior;
+use yii\db\ActiveRecord;
+
+/**
+ * Class Client
+ * @package common\models
+ */
+class Client extends ActiveRecord
+{
+	const STATUS_DELETED = 0;
+
+	const STATUS_ACTIVE = 1;
+
+	/**
+	 * @inheritdoc
+	 */
+	public function behaviors()
+	{
+		return [
+			[
+				'class' => TimestampBehavior::className(),
+				'createdAtAttribute' => 'created_at',
+				'updatedAtAttribute' => 'updated_at',
+				'value'   => function() { return time();},
+			],
+		];
+	}
+}

+ 21 - 1
common/models/User.php

@@ -69,7 +69,27 @@ class User extends ActiveRecord implements IdentityInterface
      */
     public static function findIdentityByAccessToken($token, $type = null)
     {
-        throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.');
+    	$uid = null;
+    	switch ($type)
+		{
+			case 'yii\filters\auth\HttpBearerAuth':
+				if(!($uid = AccessToken::getUidByApiToken($token)))
+					throw new \yii\web\UnauthorizedHttpException("token is invalid.");
+				break;
+
+			case 'common\filters\auth\BearerAuth':
+				$uid = 1;
+				preg_match('/^(.*?):(.*?)$/', $token, $matches);
+				$check = new \components\Secret;
+				$check->checkSignature($matches[1], $matches[2]);
+				break;
+
+			default:
+				throw new \yii\web\UnauthorizedHttpException("token is invalid.");
+		}
+
+		return static::findOne(['id' => $uid, 'status' => self::STATUS_ACTIVE]);
+        //throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.');
     }
 
     /**

+ 123 - 0
components/Secret.php

@@ -0,0 +1,123 @@
+<?php
+namespace components;
+
+use Yii;
+
+/**
+ * Class Secret
+ * @package components
+ */
+class Secret
+{
+	public $showErrorInfo = true;
+
+	private $_contentType = 'application/json';
+
+	private $_timeout = 300;
+
+	private $_contentMd5 = '';
+
+	private $_signatureMethod = 'md5';
+
+	private $_signatureVersion = '1.0';
+
+	private $_serverSignature = '';
+
+	private $_clientSignature = '';
+
+	/**
+	 * checkSignature
+	 * @param $client_id
+	 * @param $clientSignature
+	 */
+	public function checkSignature($client_id, $clientSignature)
+	{
+		$this->_clientSignature = $clientSignature;
+
+		$cone = $this->findOne($client_id);
+		if (!$cone)
+			static::throwError("client id avail.");
+
+		$sKey = isset($client_id->client_key) ? $client_id->client_key : '';
+
+		$headers = Yii::$app->request->getHeaders();
+		if (!$headers)
+			static::throwError("headers format avail.");
+
+		$strVerb = $headers->get('verb');
+		if (!$strVerb || !in_array((strtoupper($strVerb)), ['POST', 'GET']))
+			static::throwError("headers verb avail.");
+
+		$strMd5 = $headers->get('content-md5');
+		if (!$this->checkContentMd5($strVerb, $strMd5, Yii::$app->request->getBodyParams()))
+			static::throwError("headers content md5 avail.");
+
+		$strContentType = $headers->get('content-type');
+		if ($strContentType !== $this->_contentType)
+			static::throwError("headers content type avail.");
+
+		$strDate = strtotime($headers->get('date'));
+		if ($strDate == false || $strDate-time() > 0 || time()-$strDate > $this->_timeout)
+			static::throwError("headers date avail.");
+
+		$strSM = $headers->get('signature-method');
+		if (strtoupper($strSM) != strtoupper($this->_signatureMethod))
+			static::throwError("headers signature method avail.");
+
+		$strSV = $headers->get('signature-version');
+		if ($strSV !== $this->_signatureVersion)
+			static::throwError("headers signature version avail.");
+
+		$str = "{$strVerb}\n\n{$strMd5}\n{$strContentType}\n{$strDate}\n{$strSM}\n{$strSV}\n\n{$sKey}\n";
+		$base_sha1 = base64_encode(hash_hmac("sha1", $str, $sKey . '&', true));
+
+		$this->_serverSignature = md5($base_sha1);
+		if ($this->_serverSignature != '' && $this->_serverSignature !== $this->_clientSignature)
+			static::throwError("Signature verification failed.");
+
+		return true;
+	}
+
+	/**
+	 * findOne
+	 * @param $client_id
+	 */
+	protected function findOne($client_id = false)
+	{
+		$model = new \common\models\Client();
+		return $model::findOne(['client_id' => $client_id]);
+	}
+
+	/**
+	 * checkContentMd5
+	 * @param $verb
+	 * @param $md5
+	 * @param $post
+	 * @return bool
+	 */
+	protected function checkContentMd5($verb, $md5, $post)
+	{
+		if ($verb == 'GET' && $md5 != '')
+			return false;
+
+		ksort($post);
+		$this->_contentMd5 = md5(json_encode($post));
+		if ( strtolower($md5) != strtolower($this->_contentMd5))
+			return false;
+
+		return true;
+	}
+
+	/**
+	 * throwError
+	 * @param $msg
+	 * @throws \yii\web\UnauthorizedHttpException
+	 */
+	public function throwError($msg)
+	{
+		if ($this->showErrorInfo !== true)
+			throw new \yii\web\UnauthorizedHttpException('Signature error');
+
+		throw new \yii\web\UnauthorizedHttpException($msg);
+	}
+}