6. 网关主类增加token验证:
(1). 增加token验证辅助函数:
app\Lib\TokenValidator.php:
namespace App\Lib;
use Firebase\JWT\JWT;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Context\Context;
use Swoft\Redis\Redis;
/**
* @Bean(name="TokenValidator")
*/
class TokenValidator {
public function checkToken(callable $callback)
{
// 规则暂定是url参数:url?token=xxx
$getToken = Context::get()->getRequest()->get("token", "");
if ($getToken == '') {
throw new \Exception("token is not!");
}
// 返回的是一个对象,用array转换一下
// array(1) {["username"] => string(6) "shenyi"}
$token = (array)JWT::decode($getToken, 'abcde', ['HS256']);
if (empty($token['username'])) {
throw new \Exception("username is not!");
}
$key = 'token_' . $token['username'];
if (empty(Redis::get($key)) || (Redis::get($key) !== $getToken)) {
throw new \Exception("token is expired!");
}
// 先写上,后面会用到.把token值传出去,想办法传递用户身份信息到rpc服务中
return $callback(['token' => $token]);
}
}
注:
①. 验证逻辑:
a. 先通过JWT来验证token,取出库里面的信息.
b. 如果信息正确,去redis中去取出值进行比对.
c. 如果不正确,代表这个过期了.
(2). 网关主类:
app\Http\main.php:
namespace App\Http;
use App\Lib\CustomizeRateLimiter;
use App\Lib\EnvConfig;
use App\Lib\ServiceHelper;
use App\Lib\ServiceSelector;
use App\Lib\TokenValidator;
use Swoft\Bean\Annotation\Mapping\Inject;
use Swoft\Bean\BeanFactory;
use Swoft\Http\Server\Annotation\Mapping\Controller;
use Swoft\Http\Server\Annotation\Mapping\RequestMapping;
use Swoft\Http\Server\Annotation\Mapping\RequestMethod;
/**
* @Controller()
*/
class main {
...
/**
* @RequestMapping("/{prefix}/{service}/{method}", method={RequestMethod::POST, RequestMethod::OPTIONS})
*/
public function test(string $prefix, string $service, string $method)
{
$param = jsonParams();
try {
$serviceList = $this->servicHelper->getService($this->envConfig->getServicePrefix() . "." . $prefix);
$getService = $this->selector->selectByRoundRobin($serviceList);
$host = "tcp://" . $getService['Address'] . ':' . $getService['Port'];
$tags = $this->servicHelper->parseTags($getService);
$callRpc = function () use ($host, $tags, $service, $method, $param){
return requestRPC($host, choose($tags['NAMESPACE'], $this->envConfig->getNamespace()) . "\\". $service, $method, $param);
};
// 是否需要验证jwt
if (!empty($tags['tokenValid'][$service]) && in_array($method, $tags['tokenValid'][$service])) {
/**
* @var TokenValidator $tokenValidator
*/
$tokenValidator = BeanFactory::getBean("TokenValidator");
return $tokenValidator->checkToken($callRpc);
}
// 如果设置了限流,就执行限流函数,否则直接执行RPC目标调用
if (!empty($tags['rateLimiter'])) {
/**
* 下面方法没有提示,加这句
* @var CustomizeRateLimiter $rateLimiter
*/
$rateLimiter = BeanFactory::getBean("CustomizeRateLimiter");
// 传前面两个参数的作用,主要是做日志记录
$result = $rateLimiter->checkRate($service, $method, $callRpc, $tags['rateLimiter']);
} else {
$result = $callRpc();
}
} catch (\Exception $e) {
$result = ["error" => $e->getMessage()];
}
return $result;
}
}
注:
①. 访问POST:http://192.168.33.88:8306/course/ICourse/get?token=111,只有带token才能访问通过.
②. 问题:
token验证通过后,直接返回了,怎么执行下一步的限流呢? => 增加中间件处理