Skip to content

Commit

Permalink
add: 优化api签名中间件及一些调整
Browse files Browse the repository at this point in the history
  • Loading branch information
xbpk3t committed Feb 10, 2021
1 parent 094c873 commit 46c9a67
Show file tree
Hide file tree
Showing 30 changed files with 395 additions and 86 deletions.
1 change: 1 addition & 0 deletions Modules/Admin/Middleware/LogOperation.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Modules\Admin\Utils\Admin;
use Modules\Admin\Entities\OperationLog;

// 记录后台操作
class LogOperation
{
/**
Expand Down
Empty file added Modules/Api/Middleware/.gitkeep
Empty file.
5 changes: 4 additions & 1 deletion Modules/Api/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
'limit' => config('api.rate_limits.sign.limit'),
'expires' => config('api.rate_limits.sign.expires'),
];
$middleware1 = ['middleware' => ['api.throttle']];
$middleware1 = ['middleware' => [
'api.throttle',
// 'api.signature'
]];
$middleware2 = ['middleware' => [
'api.throttle',
'jwt.auth',
Expand Down
Empty file added Modules/Common/Console/.gitkeep
Empty file.
23 changes: 23 additions & 0 deletions Modules/Common/Entities/Base/Json.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php


namespace Modules\Common\Entities\Base;


use Illuminate\Contracts\Database\Eloquent\CastsAttributes;

class Json implements CastsAttributes
{

public function get($model, string $key, $value, array $attributes)
{
// TODO: Implement get() method.
return json_decode($value, true);
}

public function set($model, string $key, $value, array $attributes)
{
// TODO: Implement set() method.
return json_encode($value);
}
}
Empty file.
8 changes: 3 additions & 5 deletions Modules/Common/Utils/Base/RedisUtils.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public function isKeyExist(string $key): bool
*
* @return string
*/
public function deleteRedisKeys(string ...$redisKeys)
public function deleteRedisKeys(string ...$redisKeys): bool
{
try {
$list = $this->matchKeys($redisKeys);
Expand All @@ -49,12 +49,10 @@ public function deleteRedisKeys(string ...$redisKeys)

$this->redis->del($list);

return 'success';
return true;
} catch (\Exception $e) {
return 'fail';
return false;
}

// dd($redisKeyList);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion Modules/Common/Utils/Coupon/Coupon.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Modules\Common\Utils\Coupon;

use App\Models\V1\CouponList;
use Modules\Common\Utils\Coupon\CouponList;

class Coupon extends CouponTemplate implements CouponInterface
{
Expand Down
20 changes: 20 additions & 0 deletions Modules/Common/Utils/Coupon/CouponList.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php


namespace Modules\Common\Utils\Coupon;

use Illuminate\Database\Eloquent\Model;
use Modules\Common\Entities\Base\Json;

// 如果需要优惠券模块,把该model放到对应文件夹下
class CouponList extends Model
{
protected $table = 'tz_coupon_list';

protected $fillable = ['user_id', 'coupon_id', 'coupon_name', 'coupon_content', 'expires', 'price', 'type', 'extend'];

protected $casts = [
// 红包extend强转json
'extend' => Json::class
];
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace Modules\Common\Utils\Filter;
namespace Modules\Common\Utils\SensitiveWord;

interface Filter
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace Modules\Common\Utils\Filter;
namespace Modules\Common\Utils\SensitiveWord;

trait FilterProcessChain
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace Modules\Common\Utils\Filter;
namespace Modules\Common\Utils\SensitiveWord;

/**
* Class HashMap.
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace Modules\Common\Utils\Filter;
namespace Modules\Common\Utils\SensitiveWord;

/**
* Class SensitiveFilter.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<?php

namespace Modules\Common\Tests\Feature;
namespace Modules\Common\Utils\SensitiveWord\tests;

use Tests\TestCase;
use Modules\Common\Utils\Filter\SensitiveFilter;
use Modules\Common\Utils\Filter\FilterProcessChain;
use Modules\Common\Utils\SensitiveWord\SensitiveFilter;
use Modules\Common\Utils\SensitiveWord\FilterProcessChain;

/**
* @coversNothing
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php


namespace Modules\Common\Utils\Signature\Exception;


class InvalidSignatureException extends \Exception
{

}
174 changes: 174 additions & 0 deletions Modules/Common/Utils/Signature/Middleware/SignatureMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
<?php

namespace Modules\Common\Utils\Signature\Middleware;

use Closure;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Facades\Cache;
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
use Illuminate\Support\Facades\Redis;
use Modules\Common\Utils\Signature\Exception\InvalidSignatureException;
use \Illuminate\Http\Request;

class SignatureMiddleware extends Middleware
{
/**
* The Laravel Application.
*/
protected $nonceKey = 'api:nonce:';

const TIME_OUT = 1800;

protected $signKeys = [
'app_id',
'timestamp',
'nonce',
'http_method',
'http_path',
];

/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
*
* @return mixed
* @throws InvalidSignatureException
*/
public function handle($request, \Closure $next)
{
$this->validSign($request);

return $next($request);
}

/**
* Get sinature.
*
* @param array $params
* @param $secret
*
* @return string
*/
public function sign(array $params, string $secret)
{
$params = array_filter($params, function ($value, $key) {
return in_array($key, $this->signKeys);
}, ARRAY_FILTER_USE_BOTH);

ksort($params);

return hash_hmac('sha256', http_build_query($params, null, '&'), $secret);
}

/**
* Validate signature.
*
* @param \Illuminate\Http\Request $request
*
* @return bool
* @throws InvalidSignatureException
*/
public function validSign(Request $request = null): bool
{
if (!$request->has('timestamp') || !$request->has('nonce') || !$request->has('sign')) {
throw new InvalidSignatureException('缺少接口签名参数');
}

$timestamp = $request->input('timestamp', 0);
$nonce = $request->input('nonce');
$sign = $request->input('sign');
$signParams = $request->input();
$secret = config('api-custom.signature.secret');

$signParams = \array_merge($signParams, [
'http_method' => $request->method(),
'http_path' => $request->getPathInfo(),
]);

$this->validTimestamp($timestamp)
->validNonce($nonce)
->validHashMac($signParams, $secret, $sign);

$this->setNonceCache($nonce);

return true;
}

/**
* Validate hmac.
*
* @param $params array
* @param $secret string
* @param $hmac string
*
* @return SignatureMiddleware
* @throws InvalidSignatureException
*/
private function validHashMac(array $params, string $secret, string $sign)
{
if (\is_null($sign) || ! hash_equals($this->sign($params, $secret), $sign)) {
throw new InvalidSignatureException('Invalid Signature');
}

return $this;
}

/**
* Validate timestamp.
*
* @param $time
*
* @return $this
* @throws InvalidSignatureException
*/
private function validTimestamp($time)
{
$time = \intval($time);
$currentTime = time();

if ($time <= 0 || $time > $currentTime || $currentTime - $time > self::TIME_OUT) {
throw new InvalidSignatureException('Time out.');
}

return $this;
}

/**
* Validate nonce.
*
* @param $nonce
*
* @return $this
* @throws InvalidSignatureException
*/
private function validNonce($nonce)
{
if (\is_null($nonce) || Cache::has($this->getNonceCacheKey($nonce))) {
throw new InvalidSignatureException('Not nonce');
}

return $this;
}

/**
* Create nonce cache.
*
* @param $nonce
*/
private function setNonceCache($nonce): bool
{
// todo unittest里Cache无法set值,但是会返回true
return Cache::add($this->getNonceCacheKey($nonce), 1, self::TIME_OUT / 60);
}

/**
* @param $nonce
* @return string
*/
private function getNonceCacheKey($nonce): string
{
return $this->nonceKey.$nonce;
}
}
11 changes: 11 additions & 0 deletions Modules/Common/Utils/Signature/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# api-signature

用api签名来防止重放攻击的中间件

## 使用

可以参考单元测试的测试用例

1. 把指定secret给客户端,用来做salt
2. nonce可以由客户端自己生成
3. sign使用sha256,相比sha1和md5更安全,相比sha3更快
7 changes: 7 additions & 0 deletions Modules/Common/Utils/Signature/config/config.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

return [
'signature' => [
'secret' => env('API_SIGNATURE_SECRET', 'S4yrbflbANL517')
]
];
Loading

0 comments on commit 46c9a67

Please sign in to comment.