注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

阿飘的博客

十里平湖霜满天 寸寸青丝愁华年

 
 
 

日志

 
 

基于 PHP & MySQL 搭建 OAuth Server  

2011-11-25 16:12:45|  分类: php |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

想了一下,没有想到好的应用场景,干脆就使用 RFC5849 中的例子吧。这个例子大概的意思是:


Jane (用户,资源的所有者) 将自己度假的照片 (受保护资源) 上传到了图片分享网站A (服务提供方).
 
她现在想要在另外一个网站B (Client, 消费方) 在线打印这些照片. 一般情况下, Jane 需要使用自己的用户名和密码登陆网站A.
 
但是, Jane 并不希望将自己的用户名和密码泄露给网站B. 可是网站B需要访问图片分享网站A的图片并将其打印出来.

首先,我们再虚拟机上面搭建三个虚拟主机。我这里搭建的三个主机是:

8
# 服务提供方 Service Provider 服务提供服务器, 提供受保护资源
www.service.com
 
# 服务提供方 Service Provider OAuth认证服务器,进行请求认证
auth.service.com
 
# 消费方 Consumer 客户应用服务器, 用来发起认证请求
www.demo.com

配合上面介绍的应用场景,www.service.com 相当于网站A,而 www.demo.com 则相当于网站B.

接下来,我们为网站 A 虚拟一个用户 Jane,并将其用户名和密码以及她的照片保存在 MySQL 数据库中。

先创建一个数据库,名曰:photo, 在其中新建一个表user:


CREATE DATABASE `photo`;
 
CREATE TABLE IF NOT EXISTS `user` (
  `userId` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  `userName` varchar(20) NOT NULL COMMENT '用户名',
  `password` char(32) NOT NULL COMMENT '会员密码',
  PRIMARY KEY (`userId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户信息表' AUTO_INCREMENT=1 ;

用户有了,现在给用户创建一个表,用来存储用户照片。新建一个表“image”:


CREATE TABLE IF NOT EXISTS `image` (
  `imageId` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '图片Id',
  `userId` int(11) unsigned NOT NULL COMMENT '用户Id',
  `imagePath` varchar(255) NOT NULL COMMENT '图片路径',
  PRIMARY KEY (`imageId`),
  KEY `userId` (`userId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='图片表' AUTO_INCREMENT=1 ;

数据表有了,现在填充一些数据:


INSERTINTO`photo`.`user` (
 `userId` ,
 `userName` ,
 `password`
)
VALUES (
 '1','jane', MD5('123456')
);
 
INSERTINTO`photo`.`image` (
 `imageId` ,
 `userId` ,
 `imagePath`
)
VALUES (
 NULL ,'1','path/to/jane/image.jpeg'
);

由于 auth.service.com 认证服务器需要提供应用程序认证服务,所以需要创建一个表存储应用程序信息。实际上,还需要一些其他的相关的数据表。

我们这里使用的是 MySQL 数据库,打开浏览器,访问 http://auth.service.com/oauth-php/library/store/mysql/install.php 来进行数据表的安装。事先需要编辑 install.php 进行数据库配置。安装完毕,请将该文件的数据库连接部分重新注释掉。

下面来实现OAUTH服务器端的应用注册功能。

首先在 oauth.service.com 服务器下新建一个 config.inc.php 文件,文件内容如下:


<?php
// 数据库连接信息
$dbOptions = array(
    'server'   => 'localhost',
    'username' => 'root',
    'password' => '123456',
    'database' => 'photo'
);
?>

该文件的主要作用是保存数据库连接信息。然后继续新建一个 “oauth_register.php” 文件,文件内容如下:


<?php
// 当前登录用户
$user_id = 1;
 
// 来自用户表单
$consumer = array(
    // 下面两项必填
    'requester_name'         => 'Fising',
    'requester_email'        => 'Fising@qq.com',
 
    // 以下均为可选
    'callback_uri'           => 'http://www.demo.com/oauth_callback',
    'application_uri'        => 'http://www.demo.com/',
    'application_title'      => 'Online Printer',
    'application_descr'      => 'Online Print Your Photoes',
    'application_notes'      => 'Online Printer',
    'application_type'       => 'website',
    'application_commercial' => 0
);
 
include_once 'config.inc.php';
include_once 'oauth-php/library/OAuthStore.php';
 
// 注册消费方
$store = OAuthStore::instance('MySQL', $dbOptions);
$key   = $store->updateConsumer($consumer, $user_id);
 
// 获取消费方信息
$consumer = $store->getConsumer($key, $user_id);
 
// 消费方注册后得到的 App Key 和 App Secret
$consumer_id     = $consumer['id'];
$consumer_key    = $consumer['consumer_key'];
$consumer_secret = $consumer['consumer_secret'];
 
// 输出给消费方
echo 'Your App Key: ' . $consumer_key;
echo '<br />';
echo 'Your App Secret: ' . $consumer_secret;
?>

这时候,通过浏览器访问:http://auth.service.com 就可以自动注册一个应用(其实就是一个消费方Client)。并且将该应用的 App Key 和 App Secret呈现给你。下面 www.demo.com 站点将使用这2个字符串进行认证。所以现在先把这两个值保存起来备用。

看到的页面应该类似于:

1
2
Your App Key: de94eb65317c0d7a00af1261fc26882c04df0f850
Your App Secret: 7769ae71e703509a92c7f3816a9268af

这样,消费方注册功能就完成了。

接下来,消费方 www.demo.com 就可以使用这个 App Key 和 App Secret,向认证服务器请求未授权的 Request token 了。这一步需要做两件事情:① 消费方 www.demo.com 向 OAuth Server 也就是 auth.service.com 请求未授权的 Request token;② OAuth Server 处理消费方的请求,生成并将未授权的 Request token 返回给消费方;

先来实现第②件任务。

在认证服务器 auth.service.com 的根目录下新建一个文件”request_token.php”, 文件内容是:


<?php
include_once 'config.inc.php';
include_once 'oauth-php/library/OAuthStore.php';
include_once 'oauth-php/library/OAuthServer.php';
 
$store = OAuthStore::instance('MySQL', $dbOptions);
 
$server = new OAuthServer();
$server->requestToken();
exit();
?>

现在认证服务器已经可以响应消费方请求“未授权的token”了。

这里要特别注意一点,如果测试的时候,消费方和服务提供方在同一台服务器,就像我现在的情况,三个服务器都在同一个虚拟机里,那么要注意,客户端请 求的时候,可能发生找不到主机的问题。为啥?因为服务器找不到虚拟的 auth.service.com 认证服务器。怎么解决呢?

Windows下面,我们可以设置 hosts 主机表文件,使某一域名的指向某一固定IP地址。Linux下面也有类似的主机表文件,它的位置是 /etc/hosts,接下来如何做,这里就不用讲啦。

现在来实现第①件任务。

首先在消费方数据库服务器将认证服务器添加到消费方的 OAuthStore 中。这里的数据库安装方式与服务方相同,不再赘述。

然后,我们再消费方服务器 www.demo.com 的根目录添加一个 “config.inc.php” 文件,保存消费方自己的数据库连接信息。我这里由于使用的是虚拟主机,恰好消费方和认证服务器的数据库连接参数是一样的。实际情况可能不是这样。文件的内 容与上面的类似:


<?php
// 数据库连接信息
$dbOptions = array(
    'server'   => 'localhost',
    'username' => 'root',
    'password' => '123456',
    'database' => 'photo'
);
?>

然后,在消费方服务器根目录继续添加一个文件,add_server.php, 用来向消费方的数据库中存储认证服务器的信息。其实一般的,这个信息可能会直接写在配置文件里,不过,oauth-php提供了更加强大的数据库的存储方案而已。该文件的内容是:


<?php
include_once 'config.inc.php';
include_once 'oauth-php/library/OAuthStore.php';
 
$store = OAuthStore::instance('MySQL', $dbOptions);
 
// 当前用户的ID, 必须为整数
$user_id = 1;
 
// 服务器描述信息
$server = array(
    'consumer_key'      => 'de94eb65317c0d7a00af1261fc26882c04df0f850',
    'consumer_secret'   => '7769ae71e703509a92c7f3816a9268af',
    'server_uri'        => 'http://auth.service.com/',
    'signature_methods' => array('HMAC-SHA1', 'PLAINTEXT'),
    'request_token_uri' => 'http://auth.service.com/request_token.php',
    'authorize_uri'     => 'http://auth.service.com/authorize.php',
    'access_token_uri'  => 'http://auth.service.com/access_token.php'
);
 
// 将服务器信息保存在 OAuthStore 中
$consumer_key = $store->updateServer($server, $user_id);
?>

这样,通过浏览器访问一下该文件,http://www.demo.com/add_server.php, 服务器的相关信息就会被保存起来了。用于生产环节时,这里可能是一个简单的管理系统,可以用来管理认证服务器列表。注意,上面文件里的 key 和 secret 正是我们之前在认证服务器 http://auth.service.com 注册消费方应用时得到的。

有了认证服务器的相关信息,我们现在可以去获取“未认证的token”了。在 www.demo.com 根目录新建一个文件 index.php:


<?php
if(isset($_GET['req']) && ($_GET['req'] == 1)){
    include_once 'config.inc.php';
    include_once 'oauth-php/library/OAuthStore.php';
    include_once 'oauth-php/library/OAuthRequester.php';
 
    $store = OAuthStore::instance('MySQL', $dbOptions);
 
    // 用户Id, 必须为整型
    $user_id = 1;
 
    // 消费者key
    $consumer_key = '286cec927c4c5482e75d80759e9fdd8904df10e2f';
 
    // 从服务器获取未授权的token
    $token = OAuthRequester::requestRequestToken($consumer_key, $user_id);
    var_dump($token);
    die();
}
else{
?>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>测试页面</title>
    </head>
 
    <body>
    <p>消费放测试页面,点击下面的按钮开始测试</p>
    <input type="button" name="button" value="Click Me" id="RequestBtn"/>
    <script type="text/javascript">
    document.getElementById('RequestBtn').onclick = function(){
        window.location = 'index.php?req=1';
    }
    </script>
    </body>
    </html>
<?php
}
?>

现在,通过浏览器访问 www.demo.com/index.php页面,然后点击页面上的“Click Me”按钮,开始向auth.service.com服务器请求“未授权的token”。如果最后结果显示类似于:

1
array(2) { ["authorize_uri"]=> string(37) "http://auth.service.com/authorize.php" ["token"]=> string(41) "dc8e8df797d9737b0acfe7a8b549005604df5e485" }

那么恭喜你,获取“未授权的token”这一步,已经顺利完成了。

接下来,根据 OAuth 验证的流程,应该是重定向用户浏览器到 auth.service.com 进行 token 授权。

在 auth.demo.com 服务器根目录新建一个文件 authorize.php, 代码如下:


<?php
session_start();
 
if (empty($_SESSION['authorized']))
{
    $uri = $_SERVER['REQUEST_URI'];
    header('Location: /login.php?goto=' . urlencode($uri));
    exit();
}
 
include_once 'config.inc.php';
include_once 'oauth-php/library/OAuthStore.php';
include_once 'oauth-php/library/OAuthServer.php';
 
//登陆用户
$user_id = 1;
 
// 取得 oauth store 和 oauth server 对象
$store = OAuthStore::instance('MySQL', $dbOptions);
$server = new OAuthServer();
 
try
{
    // 检查当前请求中是否包含一个合法的请求token
    // 返回一个数组, 包含consumer key, consumer secret, token, token secret 和 token type.
    $rs = $server->authorizeVerify();
 
    if ($_SERVER['REQUEST_METHOD'] == 'POST')
    {
        // 判断用户是否点击了 "allow" 按钮(或者你可以自定义为其他标识)
        $authorized = array_key_exists('allow', $_POST);
 
        // 设置token的认证状态(已经被认证或者尚未认证)
        // 如果存在 oauth_callback 参数, 重定向到客户(消费方)地址
        $server->authorizeFinish($authorized, $user_id);
 
        // 如果没有 oauth_callback 参数, 显示认证结果
        // ** 你的代码 **
    }
    else
    {
        echo 'Error';
    }
}
catch (OAuthException $e)
{
    // 请求中没有包含token, 显示一个使用户可以输入token以进行验证的页面
    // ** 你的代码 **
}
?>

如果用户未登录,则要求先行登陆才能进行授权操作。

  评论这张
 
阅读(950)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017