基于PHP构建OAuth 2.0 认证平台

http://www.jouhu.com/blog/?p=2024


基于PHP构建OAuth 2.0 认证平台

各大门户都推出了三方API,如Google,Facebook,QQ,Sina,Alibaba等等,本文来探讨一下如何为我们的项目部署OAuth的问题。本文主要参考Fising兄的文章,说的更直白点,基本从Fising兄那里抄袭而来,也算是给自己做个记录,希望Fising兄看到后,不要介意。另外,您的文章的确写的非常好,让人敬佩。

至于为什么要使用oauth呢,fising兄做了一个非常恰当的比喻:

服务提供方 SP 好比一个封闭院子,只有持卡人才能进入,用户 U 就是持卡人之一。而消费方 C 没有持卡,通常情况下是不能进入的。但是有一天,由于特殊原因,U 需要 C 帮忙去 SP 那里取一样东西。这个时候问题就来了: C 没有持卡,不能进去院子,而 U 又不能把卡直接给 C (卡上面有很多个人机密信息,不方便外泄哦)。怎么办呢?

哦,对了,U 可以带着 C 去门口,告诉SP:这个人是我认识的,他需要进去帮我拿我的一样东西,请予放行。这样,U 既不用将带有个人私密信息的门卡交给 C,C 也通过验证拿到了属于 U 的东西。

有的人要问了,是不是下次 C 想要再进 SP 的拿 U 的东西的话,是不是就不用 U 的指引了呢?人类社会的情况通常是这样的。可惜,在 HTTP 的世界里,由于 HTTP 是无状态的协议,因此,SP 仍然不会认识 C。所以,每次 C 想要取东西,总是需要 U 的指引。是不是很麻烦呢?呵呵。但是为了安全,麻烦一点又有什何妨!

一些官方的比喻是:

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

准备工作:

1 PHP + MYSQL 环境
2 libcurl的支持(因为oauth-php里面使用到了curl库)
OAuth-PHP项目代码

由于我使用的是xampp环境,第一项满足了,第二项只需要在php.ini文件中将libcurl.so前面的;去掉,重新启动apache即可。
第三项需要下载oauth-php代码到本地。

开始:

由于本文在本地localhost实现,里面涉及到了几方的资源分享问题。我暂时放在两个目录:

1 oauthdemo目录

2 oauthphp目录

本文中需要在数据库中存储用户信息,下面需要建立一些基本数据库了:

1 SQL,创建数据库photo,并建立表user和image,并填一些初始数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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 ;

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'
);

2 需要为OAuth-php建立基本的数据表信息。

在目录(我的机器,其他机器类推一下):

E:xampphtdocsoauthphpoauth-phplibrarystoremysql

里面有install.php文件
去掉连接mysql的注释部分,代码如下了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
    // some code

<?php

/**
 * Installs all tables in the mysql.sql file, using the default mysql connection
 */


/* Change and uncomment this when you need to: */


mysql_connect ( 'localhost' ,  'root' ) ;
if  ( mysql_errno ( ) )
{
     die ( ' Error ' . mysql_errno ( ) . ': ' . mysql_error ( ) ) ;
}
mysql_select_db ( 'oauthdemo' ) ;


$sql  =  file_get_contents ( dirname ( __FILE__ )  .  '/mysql.sql' ) ;
$ps   =  explode ( '#--SPLIT--' ,  $sql ) ;

foreach  ( $ps  as  $p )
{
     $p  =  preg_replace ( '/^s*#.*$/m' ,  '' ,  $p ) ;
    
     mysql_query ( $p ) ;
     if  ( mysql_errno ( ) )
     {
         die ( ' Error ' . mysql_errno ( ) . ': ' . mysql_error ( ) ) ;
     }
}

?>

然后在浏览器中运行:

http://localhost/oauthphp/oauth-php/library/store/mysql/install.php

就完成了数据库的初始化。

然后按照Fishing兄的博客,完成相应的php文件。这里转过来,方便大家阅读:
在本文的oauthphp目录:

创建config.inc.php文件:

1
2
3
4
5
6
7
8
9
<?php
// 数据库连接信息
$dbOptions  =  array (
     'server'    =>  'localhost' ,
     'username'  =>  'root' ,
     'password'  =>  '' ,
     'database'  =>  'photo'
) ;
?>

创建oauth_register.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?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 ;
?>

在浏览器执行本php文件:

http://localhost/oauthphp/oauth-php/oauth_register.php

相当于自动注册一个应用(其实就是一个消费方Client)。并且将该应用的 App Key 和 App Secret呈现给你。下面 www.demo.com 站点将使用这2个字符串进行认证。所以现在先把这两个值保存起来备用。

即可得到:

Your App Key: 07be533fdd42a946e214a1a487b8943704f4c9501
Your App Secret: fb4c7f6a36f7941ce311d63dbf46c383

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

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

再建立文件request_token.php

1
2
3
4
5
6
7
8
9
10
11
<?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”了。

接下来,我们需要配置另外一个服务器了,就是demo服务器了。

创建oauthdemo数据库,使用

http://localhost/oauthdemo/oauth-php/library/store/mysql/install.php

来建立基本数据库表,不过其中的连接数据库需要修改为oauthdemo数据库:
文件为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php

/**
 * Installs all tables in the mysql.sql file, using the default mysql connection
 */


/* Change and uncomment this when you need to: */


mysql_connect ( 'localhost' ,  'root' ) ;
if  ( mysql_errno ( ) )
{
     die ( ' Error ' . mysql_errno ( ) . ': ' . mysql_error ( ) ) ;
}
mysql_select_db ( 'oauthdemo' ) ;


$sql  =  file_get_contents ( dirname ( __FILE__ )  .  '/mysql.sql' ) ;
$ps   =  explode ( '#--SPLIT--' ,  $sql ) ;

foreach  ( $ps  as  $p )
{
     $p  =  preg_replace ( '/^s*#.*$/m' ,  '' ,  $p ) ;
    
     mysql_query ( $p ) ;
     if  ( mysql_errno ( ) )
     {
         die ( ' Error ' . mysql_errno ( ) . ': ' . mysql_error ( ) ) ;
     }
}

?>

在目录oauthdemo创建以下文件:
config.inc.php

1
2
3
4
5
6
7
8
9
<?php
// 数据库连接信息
$dbOptions  =  array (
     'server'    =>  'localhost' ,
     'username'  =>  'root' ,
     'password'  =>  '' ,
     'database'  =>  'oauthdemo'
) ;
?>

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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'       =>  '07be533fdd42a946e214a1a487b8943704f4c9501' ,
     'consumer_secret'    =>  'fb4c7f6a36f7941ce311d63dbf46c383' ,
     'server_uri'         =>  'http://localhost/oauthphp/' ,
     'signature_methods'  =>  array ( 'HMAC-SHA1' ,  'PLAINTEXT' ) ,
     'request_token_uri'  =>  'http://localhost/oauthphp/request_token.php' ,
     'authorize_uri'      =>  'http://localhost/oauthphp/authorize.php' ,
     'access_token_uri'   =>  'http://localhost/oauthphp/access_token.php'
) ;
 
// 将服务器信息保存在 OAuthStore 中
$consumer_key  =  $store -> updateServer ( $server ,  $user_id ) ;
?>

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?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  =  '07be533fdd42a946e214a1a487b8943704f4c9501' ;
 
     // 从服务器获取未授权的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
}
?>

这里要注意,原文这里有一个小问题,也许fishing兄疏忽了,就是
$consumer_key的设定不对,想留言的,不知为何无法注册。

执行http://localhost/oauthdemo/index.php?req=1将返回:

array(2) { ["authorize_uri"]=> string(39) “http://localhost/oauthphp/authorize.php” ["token"]=> string(41) “6959214bfc5c2a946052ea805292c7cc04f4f600f” }

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

接下来,根据 OAuth 验证的流程,应该是重定向用户浏览器到 http://localhost/oauthphp/ 进行 token 授权。

在 http://localhost/oauthdemo/ 服务器根目录新建一个文件 authorize.php, 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?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以进行验证的页面
     // ** 你的代码 **
}
?>

附上代码吧:
htdocs

参考文章:

1 http://www.fising.cn/2011/03/%E4%B8%80%E6%AD%A5%E4%B8%80%E6%AD%A5%E6%90%AD%E5%BB%BA-oauth-%E8%AE%A4%E8%AF%81%E6%9C%8D%E5%8A%A1%E5%99%A8.shtml
2 http://www.fising.cn/?p=581
3 http://oauth.net/documentation/getting-started/
4 http://lds2008.blogbus.com/tag/OAuth/
5 http://iamcaihuafeng.blog.sohu.com/154447409.html
6 http://www.cnblogs.com/youxilua/archive/2011/12/29/2306790.html
7 http://www.fising.cn/2011/06/%E5%9F%BA%E4%BA%8Ephp%E7%9A%84oauth%E8%AE%A4%E8%AF%81%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E6%90%AD%E5%BB%BA.shtml

-End-


  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值