我有一个基于浏览器的应用程序(单页,AngularJS),我使用hello使用第三方登录,如谷歌,FB, Soundcloud等。
我的应用使用PHP API服务器。
让用户能够使用Google登录,但也在服务器端验证用户的好方法是什么?
我正在考虑:
- 浏览器应用通过google/fb/etc执行隐式授予 然后,我将access_token从客户端传输到服务器,然后使用,例如,google-api-php-client与我的
- 从第三方(facebook_id, email等)检索一些密钥,将其与我的数据库中的用户进行匹配,然后考虑用户身份验证?
app id
, secret和用户access_token
?使用他们的API,如/me
?(这是哪种授权类型?)另外,我应该对每个API请求执行此操作吗?或者我应该将access_token
暂时隐藏起来,并假设用户在密钥过期之前仍然有效?
一个问题是,并非所有这些提供程序都支持隐式流。但是假设他们这样做了,您为每个用户获得的access_token
将证明用户使用该系统进行了身份验证,而不一定证明他们有权调用您的 API。您仍然需要一些东西来断言"someone@gmail.com可以'读取'系统中的资源X "
你可能需要一些东西来翻译你从Google, Soundcloud等得到的任何东西到你的应用程序可以理解的令牌。一个简单的(r)格式是使用JWT。(Json Web Tokens).
App -> Intermmediary -> Soundcloud/Google
<-JWT--+ <---whavetever-+
然后:
App - (JWT) -> API
JWT易于操作、验证和验证。看到jwt.io
你可能也想看看这篇博客文章,以获得一些额外的信息(特别是关于AngularJS前端的)
上面提到的博客@eugenio-pace对于设置客户端非常有帮助。
对于服务器端,access_token
应该被验证。
SDK是(在composer中)(代码如下):
- Facebook:
- Google:
cURL
请求(不关心"google/apiclient"
) - SoundCloud:
"ise/php-soundcloud": "3.*"
"facebook/php-sdk-v4" : "4.0.*"
(当然还有其他的,只有这三个是我选择的,看起来很不错。)
上次我做了类似的事情,我犯了在每个请求上验证access_token
的错误,这对性能产生了巨大的(显然是负面的)影响。现在我只需在登录时验证它,并使用它从该服务检索用户的ID。所以,浏览器向我发送access_token A
,并说它来自Facebook,我使用Facebook的access_token
上面的sdk,我得到他们的ID,所以我知道他们是他们说他们是谁。
我建议将access_token
与expires_in
一起存储在服务器上。
(我还没有处理刷新令牌)
使用上述库验证令牌的代码:
function validateTokenFacebook($token, $id=null) {
// Performed above
// FacebookSession::setDefaultApplication($config->fb->app_id, $config->fb->secret);
$session = new FacebookSession($token);
// Fetch user info
$request = new FacebookRequest($session, 'GET', '/me');
try {
$response = $request->execute();
} catch ('Facebook'FacebookServerException $e) {
$this->mlog->err($e . "'n" . $e->getTraceAsString());
throw new AuthTokenInvalidException();
}
$graphObject = $response->getGraphObject();
$user_id = $graphObject->getProperty('id');
return array(access_token, $user_id);
}
function validateTokenGoogle($token, $id=null) {
$resp=array();
// This key isn't included in the token from hello.js, but
// google needs it
if (!array_key_exists('created', $token)) $token['created'] = $token['expires'] - $token['expires_in'];
$client = new 'Google_Client();
$client->setClientId($this->systemConfig->google->app_id);
$client->setClientSecret($this->systemConfig->google->secret);
$client->setRedirectUri($this->systemConfig->google->redirectUri);
$client->setScopes('email');
$client->setAccessToken(json_encode($token));
try {
// Send Client Request
$objOAuthService = new 'Google_Service_Oauth2($client);
$userData = $objOAuthService->userinfo->get();
return array($token['access_token'], $userData['id']);
} catch ('Google_Auth_Exception $e) {
throw new AuthException('Google returned ' . get_class($e));
}
}
function validateTokenSoundcloud($token, $id=null) {
$soundcloud = new 'Soundcloud'Service(
$this->systemConfig->soundcloud->app_id,
$this->systemConfig->soundcloud->secret,
$this->systemConfig->soundcloud->redirect);
$soundcloud->setAccessToken($access_token);
try {
$response = json_decode($soundcloud->get('me'), true);
if (array_key_exists('id', $response))
return array($access_token, $response['id']);
} catch (Soundcloud'Exception'InvalidHttpResponseCodeException $e) {
$this->mlog->err($e->getMessage());
}
throw new AuthTokenInvalidException();
}
我在上面有一些自定义类,比如Exceptions和systemConfig,但我认为它足够冗长,可以传达它们的功能。