oauth资源服务器
If you’ve ever integrated with another API that requires security (such as Twitter), you’ve probably consumed an OAuth service. In this article, I’ll explore what it takes to create your own three-legged OAuth server allowing you, for example, to create your own secure API which you can release publicly.
如果您曾经与另一个需要安全性的API集成(例如Twitter),则可能已经使用了OAuth服务。 在本文中,我将探讨如何创建自己的三足式OAuth服务器,例如,允许您创建自己的安全API,并可以将其公开发布。
When dealing with OAuth, you will typically see it implemented as a two-legged or three-legged OAuth server. The major difference between them is that two-legged authentication doesn’t involve another user. For example, if you want to access a specific user’s Twitter information, you would consume the three-legged server because an access token must be generated for the user in your application, versus just Twitter providing you a token. We’ll focus on the three-legged variety since it’s more practical for real-world use.
在处理OAuth时,通常会看到它是作为两足或三足OAuth服务器实施的。 它们之间的主要区别是两足式身份验证不涉及其他用户。 例如,如果您要访问特定用户的Twitter信息,则会消耗三足服务器,因为必须在应用程序中为该用户生成访问令牌,而Twitter只是为您提供令牌。 我们将重点介绍三足动物品种,因为它在现实世界中更实用。
We’ll use oauth-php to perform a lot of the heavy lifting for us. The library is hosted on Google Code and is not listed in Packagist, but it can still be installed using Composer. For details, check out the composer.json file in code that accompanies this article available on GitHub.
我们将使用oauth-php为我们执行很多繁重的工作。 该库托管在Google Code上,但未在Packagist中列出,但仍可以使用Composer进行安装。 有关详细信息,请在GitHub上提供本文附带的代码中检出composer.json文件。
了解流程 (Understanding the Flow)
When using a three-legged OAuth server, the typical flow that a developer would take to implement and consume the service is as follows:
使用三足式OAuth服务器时,开发人员用于实施和使用该服务的典型流程如下:
![diagram alt](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2012/12/diagram.png)
The above image, courtesy of OAuth.net, is quite complex, but in simple terms it shows the following:
上面的图片(由OAuth.net提供)非常复杂,但是简单来说,它显示了以下内容:
- The consumer requests a token from the server 使用者从服务器请求令牌
- The consumer then directs the user to a login page, passing the token with them 然后,消费者将用户引导到登录页面,并将令牌与他们一起传递
- The user logs in and is redirected back to the consumer with an access token 用户登录,并使用访问令牌重定向回使用方
- The consumer takes the access token and requests the OAuth token to use with future secure requests 使用者获取访问令牌并请求OAuth令牌与将来的安全请求一起使用
- The OAuth token is retrieved, and the developer can now make secure requests by passing the token for validation 检索OAuth令牌,开发人员现在可以通过传递令牌进行验证来提出安全请求
设置数据库 (Setting up the Database)
With the oauth-php library in an accessible location, a new database needs to be created and initialized. I’ll be using the schema script found in library/store/mysql/mysql.sql
.
由于oauth-php库位于可访问的位置,因此需要创建和初始化新的数据库。 我将使用在library/store/mysql/mysql.sql
找到的模式脚本。
If you browse through the tables, you’ll see that the oauth_server_registry
table contains a field called osr_usa_id_ref
. This is populated during the registration process by the OAuth server. It assumes you have an already existing users table that it will be related to. If you do, that’s perfect! But if not, then here is some basic SQL to create a standard user table:
如果浏览表,将会看到oauth_server_registry
表包含一个名为osr_usa_id_ref
的字段。 这是由OAuth服务器在注册过程中填充的。 假定您已有一个与之相关的用户表。 如果这样做,那就太完美了! 但是,如果没有,那么这里是一些用于创建标准用户表的基本SQL:
CREATE TABLE users (
id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(255) NOT NULL DEFAULT '',
password VARCHAR(255) NOT NULL DEFAULT '',
email VARCHAR(255) NOT NULL DEFAULT '',
created DATE NOT NULL DEFAULT '0000-00-00',
PRIMARY KEY (id)
);
创建OAuth服务器 (Creating the OAuth Server)
Let’s start writing the OAuth server. The following is common to the rest of our code, so I’ve placed it in the separate file include/common.php
:
让我们开始编写OAuth服务器。 以下是我们其余代码所共有的,因此我将其放在单独的文件include/common.php
:
<?php
require_once '../vendor/autoload.php';
session_start();
// Add a header indicating this is an OAuth server
header('X-XRDS-Location: http://' . $_SERVER['SERVER_NAME'] .
'/services.xrds.php');
// Connect to database
$db = new PDO('mysql:host=localhost;dbname=oauth', 'dbuser', 'dbpassword');
// Create a new instance of OAuthStore and OAuthServer
$store = OAuthStore::instance('PDO', array('conn' => $db));
$server = new OAuthServer();
The file adds an additional HTTP header to each request to inform clients that this is an OAuth server. Notice that it references services.xrds.php
; this file is provided with the example that comes with the oauth-php library. You should copy it from example/server/www/services.xrds.php
to the root public directory of the web server.
该文件向每个请求添加一个额外的HTTP标头,以通知客户端这是OAuth服务器。 注意,它引用了services.xrds.php
; 该文件随oauth-php库随附的示例提供。 您应该将其从example/server/www/services.xrds.php
复制到Web服务器的根公共目录。
The next few lines of code establish a connection to the database (the connection information should be updated accordingly to your own set up) and creates new instances of OAuthStore
and OAuthServer
objects provided by the library.
接下来的几行代码建立与数据库的连接(连接信息应根据您自己的设置进行相应更新),并创建库提供的OAuthStore
和OAuthServer
对象的新实例。
The set up for the OAuth server is now complete and the server is ready to be fully implemented. In the remaining examples, the includes/common.php
file must be included each time to instantiate the server.
OAuth服务器的设置现已完成,服务器已准备就绪,可以完全实施。 在其余示例中,每次都必须包含includes/common.php
文件以实例化服务器。
允许注册 (Allowing Registration)
Before developers can consume your OAuth server, they must register themselves with it. To allow this, we need to create a basic registration form. The following fields are required because they are passed to the library: requester_name
and requester_email
. The remaining fields are optional: application_uri
and callback_uri
.
开发人员在使用您的OAuth服务器之前,必须先向其注册。 为此,我们需要创建一个基本的注册表。 以下字段是必需的,因为它们已传递到库: requester_name
和requester_email
。 其余字段是可选的: application_uri
和callback_uri
。
<form method="post" action="register.php">
<fieldset>
<legend>Register</legend>
<div>
<label for="requester_name">Name</label>
<input type="text" id="requester_name" name="requester_name">
</div>
<div>
<label for="requester_email">Email</label>
<input type="text" id="requester_email" name="requester_email">
</div>
<div>
<label for="application_uri">URI</label>
<input type="text" id="application_uri" name="application_uri">
</div>
<div>
<label for="callback_uri">Callback URI</label>
<input type="text" id="callback_uri" name="callback_uri">
</div>
</fieldset>
<input type="submit" value="Register">
</form>
As I mentioned earlier, the library assumes you have existing users who want to consume your server. In the following code, I create a new user in the users
table, then retrieve the ID, and then pass it to the updateConsumer()
method creating (or updating) the consumer key and secret for this user. When you integrate this into your application, this piece should be modified and placed under your existing login process where you already know who the user is who is registering for access.
如前所述,该库假定您已有要使用服务器的用户。 在以下代码中,我在users
表中创建一个新用户,然后检索ID,然后将其传递给updateConsumer()
方法,以创建(或更新)该用户的使用者密钥和秘密。 当您将此内容集成到应用程序中时,应修改此内容并将其置于现有的登录过程下,在此过程中您已经知道谁在注册用户。
<?php
$stmt = $db->prepare('INSERT INTO users (name, email, created) ' .
'VALUES (:name, :email, NOW())');
$stmt->execute(array(
'name' => $_POST['requester_name'],
'email' => $_POST['requester_email']
));
$id = $db->lastInsertId();
$key = $store->updateConsumer($_POST, $id, true);
$c = $store->getConsumer($key, $id);
?>
<p><strong>Save these values!</strong></p>
<p>Consumer key: <strong><?=$c['consumer_key']; ?></strong></p>
<p>Consumer secret: <strong><?=$c['consumer_secret']; ?></strong></p>
On completion of the registration, the user’s new consumer key and consumer secret key are outputted. These values should be saved by the user for future use.
注册完成后,输出用户的新消费者密钥和消费者秘密密钥。 这些值应由用户保存以备将来使用。
Now that a user is registered, they can begin making requests for an access token!
现在,用户已注册,他们可以开始请求访问令牌了!
生成请求令牌 (Generating a Request Token)
Once a user has registered, they should perform an OAuth request to your request_token.php
file. This file (once again because of the library) is extremely simple:
用户注册后,他们应该对您的request_token.php
文件执行OAuth请求。 这个文件(由于库而再次出现)非常简单:
<?php
require_once 'include/oauth.php';
$server->requestToken();
The requestToken()
method takes care of validating that the user has provided a valid consumer key and signature. If the request is valid, a new request token is returned.
requestToken()
方法负责验证用户是否提供了有效的使用者密钥和签名。 如果请求有效,则返回新的请求令牌。
将请求令牌交换为访问令牌 (Exchanging the Request Token for an Access Token)
The user should be redirected to your login page once a request token has been generated. This page should expect the following URL parameters: oauth_token
and oauth_callback
.
生成请求令牌后,应将用户重定向到您的登录页面。 该页面应具有以下URL参数: oauth_token
和oauth_callback
。
The login page should retrieve the user from the users table. Once retrieved, the user ID is passed (along with the oauth_token
) to the authorizeVerify()
method provided by the library. Assuming the user has authorized the application, the ID of the logged in user is then associated with the consumer’s key allowing them secure access to this user’s data. The necessary logic of a basic login.php
might look like the following:
登录页面应从用户表中检索用户。 检索到用户ID后(连同oauth_token
)传递给库提供的authorizeVerify()
方法。 假设用户已授权该应用程序,则将已登录用户的ID与使用者的密钥相关联,从而允许他们安全地访问该用户的数据。 基本login.php
的必要逻辑如下所示:
<?php
// check if the login information is valid and get the user's ID
$sql = 'SELECT id FROM users WHERE email = :email';
$stmt = $db->prepare($sql);
$result = $stmt->exec(array(
'email' => $_POST['requester_email']
));
$row = $result->fetch(PDO::FETCH_ASSOC);
if (!$row) {
// incorrect login
}
$id = $row['id'];
$result->closeCursor();
// Check if there is a valid request token in the current request.
// This returns an array with the consumer key, consumer secret,
// token, token secret, and token type.
$rs = $server->authorizeVerify();
// See if the user clicked the 'allow' submit button (or whatever
// you choose)
$authorized = array_key_exists('allow', $_POST);
// Set the request token to be authorized or not authorized
// When there was a oauth_callback then this will redirect to
// the consumer
$server->authorizeFinish($authorized, $id);
After the user logs in, they will be redirected back to the consuming developer’s website (via the oauth_callback
parameter) with a valid token. This token and verify key can then be used in the exchange for a valid access token.
用户登录后,将使用有效令牌将其重定向回使用方开发者的网站(通过oauth_callback
参数)。 然后可以将该令牌和验证密钥用于交换有效的访问令牌。
A basic access_token.php
file looks like this:
基本的access_token.php
文件如下所示:
<?php
require_once 'include/oauth.php';
$server->accessToken();
This file is as simple as the previously created request_token.php
. The work is all done inside the accessToken()
method provided by the oauth-php library. Upon a successful request, a valid oauth_token
and oauth_token_secret
are outputted that should be stored and used with future requests to your API.
该文件与之前创建的request_token.php
一样简单。 该工作全部在oauth-php库提供的accessToken()
方法内完成。 成功请求后,将输出有效的oauth_token
和oauth_token_secret
,应将其存储并与将来对API的请求一起使用。
验证请求 (Validating a Request)
At this point, the OAuth server is up and running. But we still need to verify that a request contains a valid OAuth signature. I’ve created a basic test file that does just that:
此时,OAuth服务器已启动并正在运行。 但是我们仍然需要验证请求是否包含有效的OAuth签名。 我已经创建了一个基本的测试文件,该文件可以执行以下操作:
<?php
require_once 'includes/oauth.php';
if (OAuthRequestVerifier::requestIsSigned()) {
try {
$req = new OAuthRequestVerifier();
$id = $req->verify();
// If we have a user ID, then login as that user (for
// this request)
if ($id) {
echo 'Hello ' . $id;
}
} catch (OAuthException $e) {
// The request was signed, but failed verification
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: OAuth realm=""');
header('Content-Type: text/plain; charset=utf8');
echo $e->getMessage();
exit();
}
}
In the example, if the request is verified, I simply echo the user id of the user who logged in. I would suggest creating a re-usable method that contains this code for any API calls that require security.
在该示例中,如果请求得到验证,则仅回显已登录用户的用户ID。我建议为所有需要安全性的API调用创建一个包含此代码的可重用方法。
测试OAuth服务器 (Testing the OAuth Server)
Finally, it’s time to test the OAuth server. Below is a simple test file that performs the above steps to require a user to login and performs a secure request:
最后,是时候测试OAuth服务器了。 以下是一个简单的测试文件,该文件执行上述步骤以要求用户登录并执行安全请求:
<?php
define('OAUTH_HOST', 'http://' . $_SERVER['SERVER_NAME']);
$id = 1;
// Init the OAuthStore
$options = array(
'consumer_key' => '<MYCONSUMERKEY>',
'consumer_secret' => '<MYCONSUMERSECRET>',
'server_uri' => OAUTH_HOST,
'request_token_uri' => OAUTH_HOST . '/request_token.php',
'authorize_uri' => OAUTH_HOST . '/login.php',
'access_token_uri' => OAUTH_HOST . '/access_token.php'
);
OAuthStore::instance('Session', $options);
if (empty($_GET['oauth_token'])) {
// get a request token
$tokenResultParams = OauthRequester::requestRequestToken($options['consumer_key'], $id);
header('Location: ' . $options['authorize_uri'] .
'?oauth_token=' . $tokenResultParams['token'] .
'&oauth_callback=' . urlencode('http://' .
$_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF']));
}
else {
// get an access token
$oauthToken = $_GET['oauth_token'];
$tokenResultParams = $_GET;
OAuthRequester::requestAccessToken($options['consumer_key'],
$tokenResultParams['oauth_token'], $id, 'POST', $_GET);
$request = new OAuthRequester(OAUTH_HOST . '/test_request.php',
'GET', $tokenResultParams);
$result = $request->doRequest(0);
if ($result['code'] == 200) {
var_dump($result['body']);
}
else {
echo 'Error';
}
}
OAuth requires timestamps and signatures to be appended to each request, and once again this library will perform this for us.
OAuth要求将时间戳和签名附加到每个请求中,并且该库将再次为我们执行此操作。
The first part of the above code is configuration information that should be updated accordingly to match your needs. The user ID, consumer key, and consumer secret key are all generated during the registration process on the server.
上面的代码的第一部分是配置信息,应根据您的需要进行相应的更新。 用户ID,消费者密钥和消费者秘密密钥都是在服务器上的注册过程中生成的。
As described during the introduction to a three-legged OAuth server, the following process is performed in the above test file:
如三腿式OAuth服务器简介中所述,在上述测试文件中执行以下过程:
Ask for a request token (via the
request_token.php
file) with the consumer key使用使用者密钥要求一个请求令牌(通过
request_token.php
文件)- Upon receiving the token, redirect the user to the login page passing the token and callback URL via URL parameters 收到令牌后,将用户重定向到登录页面,并通过URL参数传递令牌和回调URL
Once the user is logged in, they are redirected back to the above test page. The test page takes the token and asks for an access token (via the
access_token.php
file)用户登录后,他们将被重定向回上述测试页面。 测试页获取令牌并要求访问令牌(通过
access_token.php
文件)Upon success, the necessary OAuth information is returned and the test file performs a secure request to
test_request.php
.成功后,将返回必要的OAuth信息,并且测试文件对
test_request.php
执行安全请求。- If all goes well a basic “Hello 1” will be displayed. 如果一切顺利,将显示基本的“ Hello 1”。
摘要 (Summary)
At this point, you should know how to create a basic OAuth server. Using the test_request.php
file as example, you can begin creating more features that are secured with Oauth! If you’d like to play around with some code, full source code for this article is available on GitHub.
此时,您应该知道如何创建基本的OAuth服务器。 以test_request.php
文件为例,您可以开始创建更多由Oauth保护的功能! 如果您想使用一些代码,可以在GitHub上获得本文的完整源代码 。
Image via Fotolia
图片来自Fotolia
And if you enjoyed reading this post, you’ll love Learnable; the place to learn fresh skills and techniques from the masters. Members get instant access to all of SitePoint’s ebooks and interactive online courses, like Jump Start PHP.
并且,如果您喜欢阅读这篇文章,您会喜欢Learnable的 ; 向大师学习新鲜技能的地方。 会员可以立即访问所有SitePoint的电子书和交互式在线课程,例如Jump Start PHP 。
Comments on this article are closed. Have a question about PHP? Why not ask it on our forums?
本文的评论已关闭。 对PHP有疑问吗? 为什么不在我们的论坛上提问呢?
oauth资源服务器