>php教程 >php手册 >基于 PHP & MySQL 搭建 OAuth Server

基于 PHP & MySQL 搭建 OAuth Server

WBOY
WBOY원래의
2016-06-06 19:54:081066검색

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 群里的损友们又开始叫了,说在等待我的关于 OAuth 服务器搭建 Demo 的介绍文章。这段时间一直很忙,人

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


群里的损友们又开始叫了,说在等待我的关于 OAuth 服务器搭建 Demo 的介绍文章。这段时间一直很忙,人一忙,偶尔的一点闲暇就想睡觉,啥也不想做,学习上确实有些懈怠。此处悔过5分钟…….

我之前写过一篇《一步一步搭建 OAuth 认证服务器》的文章,其实也就是介绍了一下 OAuth 的理解和 oauth-php 这个开源的项目,并没有做出一个演示。今天这篇文章就来做一个Demo,我们基于 PHP 来搭建一个 OAuth认证服务器。开始吧!

为了方便理解,可以先看一下在 OAuth 认证过程中的几个关键术语,这也是 RFC5849 中 “1.1.  Terminology” 小节的内容。也可以查看其中文版本。

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

1

2

3

4

5

Jane(用户,资源的所有者)将自己度假的照片(受保护资源)上传到了图片分享网站A(服务提供方).

她现在想要在另外一个网站B(Client,消费方)在线打印这些照片.一般情况下,Jane需要使用自己的用户名和密码登陆网站A.

但是,Jane并不希望将自己的用户名和密码泄露给网站B.可是网站B需要访问图片分享网站A的图片并将其打印出来.

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

1

2

3

4

5

6

7

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:

1

2

3

4

5

6

7

8

CREATE DATABASE`photo`;

CREATE TABLE IFNOTEXISTS`user`(

  `userId`int(11)unsignedNOTNULLAUTO_INCREMENT COMMENT'用户ID',

  `userName`varchar(20)NOTNULLCOMMENT'用户名',

  `password`char(32)NOTNULLCOMMENT'会员密码',

  PRIMARY KEY(`userId`)

)ENGINE=InnoDB DEFAULTCHARSET=utf8 COMMENT='用户信息表'AUTO_INCREMENT=1;

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

1

2

3

4

5

6

7

CREATE TABLE IFNOTEXISTS`image`(

  `imageId`int(11)unsignedNOTNULLAUTO_INCREMENT COMMENT'图片Id',

  `userId`int(11)unsignedNOTNULLCOMMENT'用户Id',

  `imagePath`varchar(255)NOTNULLCOMMENT'图片路径',

  PRIMARY KEY(`imageId`),

  KEY`userId`(`userId`)

)ENGINE=InnoDB DEFAULTCHARSET=utf8 COMMENT='图片表'AUTO_INCREMENT=1;

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

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 文件,文件内容如下:

1

2

3

4

5

6

7

8

9

// 数据库连接信息

$dbOptions=array(

    'server'   =>'localhost',

    'username'=>'root',

    'password'=>'123456',

    '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

// 当前登录用户

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

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”, 文件内容是:

1

2

3

4

5

6

7

8

9

10

11

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=newOAuthServer();

$server->requestToken();

exit();

?>

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

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

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

现在来实现第①件任务。

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

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

1

2

3

4

5

6

7

8

9

// 数据库连接信息

$dbOptions=array(

    'server'   =>'localhost',

    'username'=>'root',

    'password'=>'123456',

    'database'=>'photo'

);

?>

然后,在消费方服务器根目录继续添加一个文件,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

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:

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

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><span>type</script>="text/javascript">

    document.getElementById('RequestBtn').onclick=function(){

        window.location='index.php?req=1';

    }

    

    /body>

    /html>

}

?>

现在,通过浏览器访问 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, 代码如下:

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

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=newOAuthServer();

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以进行验证的页面

    // ** 你的代码 **

}

?>

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




성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.