从Symfony2中的外部Web API存储访问和刷新令牌的位置?

时间:2016-09-13 09:33:13

标签: php symfony guzzle

我创建了一个简单的Symfony2项目,允许员工登录并查看新闻和其他内容。然后我想整合一个外部系统,显示员工是否在工作。我无法控制其他系统,但我有一个Web API(REST),以便我可以检索所需的信息。

由于此GuzzleBundle中的答案,我决定使用post (stackoverflow)作为PHP HTTP客户端来获取我需要的信息。

所以我登录了Web API所需的内容:

$client = new GuzzleHttp\Client();
$req = $client->request("POST", "https://httpbin.org/login",  ['body' => ['user' => 'user', 'pw' => 'pw']]);
$response = json_decode($req->getBody()->getContents());

然后我使用了$ response提供的访问令牌:

{
    "success": true,
    "content": [
    {
        "accessToken": "x",
        "accessTokenExpires": "date",
        "refreshToken": "z",
        "refreshTokenExpires": "date"
    }]
}

要检索所需的信息,请执行以下操作:

$reqPara     = ['body' =>["accessToken" => $at, "department" => $department, "location" =>$location]];

$req = $client->request("POST", "https://httpbin.org/employees/atwork", $reqPara);
$response = json_decode($req->getBody()->getContents());

我得到了我想要的东西:

{
    "success": true,
    "content": [
    {
        "employee": "a",
        "status": "at work",
    },
    {
        "employee": "b",
        "status": "not at work",
    }
    ]
}

当然,每次我想知道员工是否在工作时我都可以登录,但是当我可以使用访问令牌时,这似乎是浪费。但是当访问令牌到期时,我需要刷新令牌来生成新的令牌但是我在哪里存储这些令牌并在以后重新使用它们?在数据库或配置文件中?它是否存在标准?

3 个答案:

答案 0 :(得分:1)

很明显,此令牌必须缓存,而不是存储在配置中。它将如何缓存 - 它完全取决于您,通常取决于您启动应用程序的频率,以及在您拥有环境时如何配置它。

这些都是有效的解决方案:存储在某个文件中,存储在数据库中,只存储在内存中(作为类的属性),存储在一些键值存储(redis,memcache)中。 / p>

如果您使用Symfony 3.1,我建议您使用CacheComponent

答案 1 :(得分:0)

将您的用户名和密码存储在parameters.yml

答案 2 :(得分:0)

Dmitry Malyshenko回答了我需要听到的内容。 "很明显,此令牌必须缓存,而不是存储在配置中。如何缓存它 - 完全取决于你......"

有效解决方案的示例:

  • 存储在某个文件中
  • 存储在数据库中
  • 只存储在内存中(作为 贵班的财产)
  • 存储在一些键值存储中(redis, 内存缓存)

我会使用CacheComponent但我目前没有在Symfony 3.1中开发,所以我决定存储在一个最自然的数据库中。首先,我在mysql数据库中创建了一个包含用户名,密码和令牌的表。然后我创建了一个专用于该表的服务。它看起来像这个简化的代码:

class ExternalSiteService
{
    public function  getUsername() {
        ...
    }

    public function getPassword() {
        ...
    }

    public function getAccessToken() {
        ...
    }

    public function setAccessToken($newAccessToken) {
        ...
    }

    public function getRefreshToken() {
        $query="SELECT "
            . "eapi.refreshtoken "
            . "FROM external_api eapi";
        $connection = $this->em->getConnection();
        $statement = $connection->prepare($query);
        $statement->execute();
        $results = $statement->fetchAll();
        if ( $results ) {
            return $results;
        }
        return false;
    }

    public function setRefreshToken($newRefreshToken) {
        $query="UPDATE external_api "
            . "SET refreshtoken = :new_refreshtoken "
            . "WHERE id=1;";
        $connection = $this->em->getConnection();
        $statement = $connection->prepare($query);
        $statement->bindValue('new_refreshtoken', $newRefreshToken);
        $statement->execute();
    }
}

然后我在控制器中做了一些逻辑,说明访问令牌无效是否使用刷新令牌,如果刷新令牌无效则执行新的登录尝试。如果我们必须获取新代码,控制器中的代码也会将令牌存储在数据库中。

    $eapiService    = $this->get('mybundle.service.externalapi');
    $reqPara     = ['body' =>["accessToken" => $at, "department" => $department, "location" =>$location]];

    $req = $client->request("POST", "https://httpbin.org/employees/atwork", $reqPara);
    $response = json_decode($req->getBody()->getContents());

    if ($response->serverErrorMessage == "Invalid access token.") {
        $req = $client->request('POST', "https://httpbin.org/getnewtokens", ['body' => ['refreshToken' => $refreshToken]]);
        $response = json_decode($req->getBody()->getContents());

        if ($response->serverErrorMessage == "Invalid refresh token.") {
            $req = $client->request('POST', 'https://httpbin.org/login', ['body' => ['user' => $user, 'pw' => $pw]]);
            $response = json_decode($req->getBody()->getContents());
            foreach ($response->content as $contentItem) {
                $eapiService->setAccessToken($contentItem->accessToken);
                $eapiService->setRefreshToken($contentItem->refreshToken);
            }
        } else {
            foreach ($response->content as $contentItem) {
                $eapiService->setAccessToken($contentItem->accessToken);
                $eapiService->setRefreshToken($contentItem->refreshToken);
            }
        }
    }
    return array('usersatwork' => $response);

我可能还会做一些事情来捕捉guzzle中的异常。