问题
I have created a simple Symfony2 project which allows employees to log in and check news and other stuff. Then I wanted to integrate an external system that shows whether the employee is at work or not. I have no control over the other system but I got an web API (REST) so that I may retrieve the information needed.
I decided to use GuzzleBundle as the PHP HTTP Client to get the information I need because of the answers in this post (stackoverflow).
So I logged in which is required by the 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());
And then I used the provided access token from $response:
{
"success": true,
"content": [
{
"accessToken": "x",
"accessTokenExpires": "date",
"refreshToken": "z",
"refreshTokenExpires": "date"
}]
}
To retrieve information needed like this:
$reqPara = ['body' =>["accessToken" => $at, "department" => $department, "location" =>$location]];
$req = $client->request("POST", "https://httpbin.org/employees/atwork", $reqPara);
$response = json_decode($req->getBody()->getContents());
And I got what I wanted:
{
"success": true,
"content": [
{
"employee": "a",
"status": "at work",
},
{
"employee": "b",
"status": "not at work",
}
]
}
And of course I can login each time I want to know if the employees is at work but that seems like a waste when I can use the access token. But then again when the access token expires I need the refresh token to generate new tokens but where do I store these tokens and re-use them later? In a database or config file? Does it exist a standard for this?
回答1:
It's obvious that this token must be cached, not stored in configuration. How it would be cached - it totally up to you and normally depends on how often you start an application, how it configured when environment you have.
These all are valid solutions: to store in some file, to store in a database, to store just in memory (as a property of your class), to store in some key-value storage (redis, memcache).
If you use Symfony 3.1 I would recommend use CacheComponent for this.
回答2:
Dmitry Malyshenko answered what I needed to hear. "It's obvious that this token must be cached, not stored in configuration. How it would be cached - it totally up to you..."
Examples of valid solutions:
- store in some file
- store in a database
- store just in memory (as a property of your class)
- store in some key-value storage (redis, memcache)
I would have used CacheComponent but I am currently not developing in Symfony 3.1 so I decided to store in a database which felt most naturally. First I created a table with username, password and tokens in a mysql-database. Then I created a service dedicated to that table. It looks something like this simplified code:
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();
}
}
Then I made some logic in the controller that says if access token not valid use refresh token, and if refresh token not valid do a new login attempt. This code in the controller also stores the tokens in the database if we have to get new ones.
$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);
I will probably also make something to catch exceptions from guzzle.
回答3:
Store your username and password inside the parameters.yml
来源:https://stackoverflow.com/questions/39466648/where-to-store-access-and-refresh-token-from-external-web-api-in-symfony2