各大門戶都推出了三方API,如Google,Facebook,QQ,Sina,Alibaba等等,本文來探討一下如何爲咱們的項目部署OAuth的問題。本文主要參考Fising兄的文章,說的更直白點,基本從Fising兄那裏抄襲而來,也算是給本身作個記錄,但願Fising兄看到後,不要介意。另外,您的文章的確寫的很是好,讓人敬佩。javascript
至於爲何要使用oauth呢,fising兄作了一個很是恰當的比喻:php
服務提供方 SP 比如一個封閉院子,只有持卡人才能進入,用戶 U 就是持卡人之一。而消費方 C 沒有持卡,一般狀況下是不能進入的。可是有一天,因爲特殊緣由,U 須要 C 幫忙去 SP 那裏取同樣東西。這個時候問題就來了: C 沒有持卡,不能進去院子,而 U 又不能把卡直接給 C (卡上面有不少我的機密信息,不方便外泄哦)。怎麼辦呢?html
哦,對了,U 能夠帶着 C 去門口,告訴SP:這我的是我認識的,他須要進去幫我拿個人同樣東西,請予放行。這樣,U 既不用將帶有我的私密信息的門卡交給 C,C 也經過驗證拿到了屬於 U 的東西。java
有的人要問了,是否是下次 C 想要再進 SP 的拿 U 的東西的話,是否是就不用 U 的指引了呢?人類社會的狀況一般是這樣的。惋惜,在 HTTP 的世界裏,因爲 HTTP 是無狀態的協議,所以,SP 仍然不會認識 C。因此,每次 C 想要取東西,老是須要 U 的指引。是否是很麻煩呢?呵呵。可是爲了安全,麻煩一點又有什何妨!mysql
一些官方的比喻是:android
Jane (用戶,資源的全部者) 將本身度假的照片 (受保護資源) 上傳到了圖片分享網站A (服務提供方).她如今想要在另一個網站B (Client, 消費方) 在線打印這些照片. 通常狀況下, Jane 須要使用本身的用戶名和密碼登錄網站A.可是, Jane 並不但願將本身的用戶名和密碼泄露給網站B. 但是網站B須要訪問圖片分享網站A的圖片並將其打印出來.web
準備工做:sql
1 PHP + MYSQL 環境
2 libcurl的支持(由於oauth-php裏面使用到了curl庫)
3 OAuth-PHP項目代碼數據庫
因爲我使用的是xampp環境,第一項知足了,第二項只須要在php.ini文件中將libcurl.so前面的;去掉,從新啓動apache便可。
第三項須要下載oauth-php代碼到本地。apache
開始:
因爲本文在本地localhost實現,裏面涉及到了幾方的資源分享問題。我暫時放在兩個目錄:
2 oauthphp目錄
本文中須要在數據庫中存儲用戶信息,下面須要創建一些基本數據庫了:
1 SQL,建立數據庫photo,並創建表user和image,並填一些初始數據。
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:\xampp\htdocs\oauthphp\oauth-php\library\store\mysql
裏面有install.php文件
去掉鏈接mysql的註釋部分,代碼以下了:
<?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文件:
<?php // 數據庫鏈接信息 $dbOptions = array( 'server' => 'localhost', 'username' => 'root', 'password' => '', '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; ?>
在瀏覽器執行本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
<?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數據庫:
文件爲:
<?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
<?php // 數據庫鏈接信息 $dbOptions = array( 'server' => 'localhost', 'username' => 'root', 'password' => '', 'database' => 'oauthdemo' ); ?>
而後,在消費方服務器根目錄繼續添加一個文件,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' => '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:
<?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, 代碼以下:
<?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