在 Symfony2 的何处存储来自外部 Web API 的访问和刷新令牌?
Where to store access and refresh token from external web API in Symfony2?
我创建了一个简单的 Symfony2 项目,它允许员工登录并查看新闻和其他内容。然后我想集成一个外部系统来显示员工是否在工作。我无法控制其他系统,但我有一个网络 API (REST) 以便我可以检索所需的信息。
我决定使用GuzzleBundle as the PHP HTTP Client to get the information I need because of the answers in this post (Whosebug)。
所以我登录了网络要求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",
}
]
}
当然,每次我想知道员工是否在工作时,我都可以登录,但是当我可以使用访问令牌时,这似乎是一种浪费。但是话又说回来,当访问令牌过期时,我需要刷新令牌来生成新令牌 但是我应该在哪里存储这些令牌并在以后重新使用它们呢?在数据库或配置文件中?是否存在这方面的标准?
将您的用户名和密码存储在 parameters.yml
很明显,这个令牌必须被缓存,而不是存储在配置中。如何缓存它 - 这完全取决于您,通常取决于您启动应用程序的频率,它在您拥有的环境中的配置方式。
这些都是有效的解决方案:存储在某些文件中,存储在数据库中,仅存储在内存中(作为 class 的 属性),存储在某些密钥中-值存储(redis、memcache)。
如果您使用 Symfony 3.1,我建议您为此使用 CacheComponent。
Dmitry Malyshenko 回答了我需要听到的内容。 "It's obvious that this token must be cached, not stored in configuration. How it would be cached - it totally up to you..."
有效解决方案示例:
- 存储在某个文件中
- 存储在数据库中
- 只存储在内存中(作为
属性 的 class)
- 存储在一些键值存储中(redis,
内存缓存)
我会使用 CacheComponent 但我目前没有在 Symfony 3.1 中开发所以我决定存储在一个感觉最自然的数据库中。首先,我在 mysql 数据库中创建了一个包含用户名、密码和令牌的 table。然后我创建了一个专门用于 table 的服务。它看起来像这个简化的代码:
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 的异常。
我创建了一个简单的 Symfony2 项目,它允许员工登录并查看新闻和其他内容。然后我想集成一个外部系统来显示员工是否在工作。我无法控制其他系统,但我有一个网络 API (REST) 以便我可以检索所需的信息。
我决定使用GuzzleBundle as the PHP HTTP Client to get the information I need because of the answers in this post (Whosebug)。
所以我登录了网络要求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",
}
]
}
当然,每次我想知道员工是否在工作时,我都可以登录,但是当我可以使用访问令牌时,这似乎是一种浪费。但是话又说回来,当访问令牌过期时,我需要刷新令牌来生成新令牌 但是我应该在哪里存储这些令牌并在以后重新使用它们呢?在数据库或配置文件中?是否存在这方面的标准?
将您的用户名和密码存储在 parameters.yml
很明显,这个令牌必须被缓存,而不是存储在配置中。如何缓存它 - 这完全取决于您,通常取决于您启动应用程序的频率,它在您拥有的环境中的配置方式。
这些都是有效的解决方案:存储在某些文件中,存储在数据库中,仅存储在内存中(作为 class 的 属性),存储在某些密钥中-值存储(redis、memcache)。
如果您使用 Symfony 3.1,我建议您为此使用 CacheComponent。
Dmitry Malyshenko 回答了我需要听到的内容。 "It's obvious that this token must be cached, not stored in configuration. How it would be cached - it totally up to you..."
有效解决方案示例:
- 存储在某个文件中
- 存储在数据库中
- 只存储在内存中(作为 属性 的 class)
- 存储在一些键值存储中(redis, 内存缓存)
我会使用 CacheComponent 但我目前没有在 Symfony 3.1 中开发所以我决定存储在一个感觉最自然的数据库中。首先,我在 mysql 数据库中创建了一个包含用户名、密码和令牌的 table。然后我创建了一个专门用于 table 的服务。它看起来像这个简化的代码:
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 的异常。