現在越來越多開放的互聯網公司提供對外的 API 介面,使得協力廠商應用開發人員可以開發基於該平臺介面的應用程式。國外有Twitter、Flicker Service等;國內的,像騰訊微博開放平臺、新浪微博開放平臺等等。

 

這些平臺介面的認證方式,無一例外的,都採取了 OAuth 來實現(Twitter原來使用的是Basic Auth方式,後來全面轉向OAuth)。

 

那麼,OAuth 是什麼?OAuth認證又有什麼好處呢?

 

OAuth 是什麼

 

關於OAuth的定義,在 OAuth官網 的首頁上,有一行大大的文字說明:

 

1 An open protocol to allow secure API authorization in a simple and standard method from desktop and web applications.

 

(OAuth) 是一個開放協定,它以一種簡單、標準的方式實現對桌面和 Web 的應用程式的安全 API 認證。

 

OAuth 1.0 協定(中文版 | 英文版)這樣介紹OAuth:「OAuth 協定致力於使網站和應用程式(統稱為消費方)能夠在無須使用者透露其認證證書的情況下,通過 API 訪問某個web服務(統稱為服務提供者)的受保護資源。更一般地說,OAuth 為 API 認證提供了一個可自由實現且通用的方法」。

 

關於 OAuth 的用途,OAuth 1.0 協定(中文版 | 英文版)上舉了一個例子:某列印服務提供者 printer.example.com(消費方),希望在無須使用者提供其照片存儲網站密碼的情況下,訪問使用者儲存在 photos.example.net(服務提供者)上的個人照片。

 

假如沒有 OAuth,使用者必須要向消費方也就是 printer 提供自己在服務提供者 photos 上的授權資料(通常是密碼),消費方利用這個授權資料,通過服務提供者的許可權驗證,從而獲得要列印的圖片。這樣看似沒有什麼問題。但是使用者的授權資料,通常在這一過程中被消費方有意或者無意地竊取或洩露,從而對使用者和服務提供者的資訊安全造成威脅。

 

而如果利用 OAuth 進行此過程的授權,使用者的授權資料並不會傳遞給協力廠商(也就是消費方,通常是App應用),而消費方只需要將使用者引導至服務提供者的授權頁面進行授權,使得消費方獲得訪問受限資源的許可權即可。而在此過程中,使用者授權過程是在服務提供者進行的,消費方並不會直接接觸到使用者的授權資料,因此一般不會造成使用者授權資料的洩密,從而既保證了使用者和服務提供者的資訊安全,又使得消費方完成了對受限資源的讀取。可謂一舉三得。

 

個人對 OAuth 授權過程的理解:服務提供者 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 的指引。是不是很麻煩呢?呵呵。但是為了安全,麻煩一點又有什何妨!

 

上面介紹了 OAuth 認證的基本思路,如果你還不理解,可以參考 OAuth認證流程圖, 或者查看騰訊微博關於OAuth認證的介紹。OAuth 官方網站就有一篇文檔教程《OAuth入門指南》,不過沒有中文版本。有興趣同學的也可以自己看看。

 

OAuth 認證授權有以下幾個特點:

 

•1. 簡單:不管是 OAuth 服務提供者還是應用開發者,都很容易於理解與使用;

•2. 安全:沒有涉及到使用者金鑰等資訊,更安全更靈活;

•3. 開放:任何服務提供者都可以實現 OAuth,任何軟體發展商都可以使用 OAuth;

那麼下面我們就作為服務提供者角色,來實現 OAuth 認證伺服器的安裝和搭建。

 

其實,很多先行者已經開發出了很多的 OAuth 消費方代碼(用戶端)和服務提供者代碼(服務端),這裡是它們的一些清單,其中包含了 .NET (C#/VB.NET), ColdFusion, JAVA, JAVAscript, Jifty, Objective-C, OCaml, Perl, PHP, Python, Ruby, Erlang 和其他語言的一些實現。

 

通過 Google,我找到了一個開源的 OAuth 服務端代碼和消費方開原始程式碼庫專案——oauth-php. 我們就借此來實現。關於OAuth,專案主頁的介紹是: OAuth Consumer And Server Library For PHP. 它包含一個完整實現的可擴展的OAuth存儲,支援 MySQL/MySQLi, Postgresql, PDO 和 Oracle 等多種存儲方式。

 

它實現了以下方法:

 

•認證進來的請求

•為出去的請求籤名

•使用 body 為請求籤名

•為多使用者管理消費方的 key 和 token(服務端和消費端)

•記錄經過類庫處理的進出的請求(可以在資料庫中進行可選配置)

很多網站都在使用oauth-php, 包括荷蘭阿姆斯特丹Mediamatic Lab實驗室出品的anyMeta CMS. 目前,oauth-php 的代碼主要由Corollarium Technologies 負責維護。

 

為你的伺服器增加 OAuth 非常簡單。你需要檢查進來的請求中 OAuth 認證細節。首先,我們需要四個控制器 controller 檔:

 

•oauth_register.php 使消費方使用者獲得 key 和金鑰

 

•request_token.php 返回一個未認證的 request token

 

•authorize.php 認證一個request token

 

•access_token.php 將認證後的 request token 置換為 access token

以下的例子,假設使用的資料儲存體是MySQL。你也可以使用其他資料庫——當你第一次使用 OAuthStore 實例的時候指定一個參數,告訴它你要使用的資料庫。

 

1 $store = OAuthStore::instance('mystore');

 

這就是假設在儲存體目錄,你有一個名為 OAuthStoremystore.php 檔。在這裡我們使用 OAuthStoreMySQL.php 檔來實現。這也就是說,我們在具現化 OAuthStore 的時候,需要這樣做:

 

1 $store = OAuthStore::instance('MySQL');

 

然後,在每個請求被處理之前,都可以檢查其是否帶有 OAuth 認證資訊:

 

if (OAuthRequestVerifier::requestIsSigned())

{

try

{

$req = new OAuthRequestVerifier();

$user_id = $req->verify();

// 如果存在 user_id, 那麼作為那個使用者角色登錄(對於本次請求)

if ($user_id)

{

// **** 在這裡新增你的代碼 ****

}

}

catch (OAuthException $e)

{

// 請求已經簽名,但是認證失敗

header('HTTP/1.1 401 Unauthorized');

header('WWW-Authenticate: OAuth realm=""');

header('Content-Type: text/plain; charset=utf8');

echo $e->getMessage();

exit();

}

}

 

每個消費方都使用 key 和金鑰的組合和 token 和 token 金鑰的組合來為它的請求進行簽名。而在此之前,消費方必須要先獲取屬於它的消費方 key 和消費方金鑰。 oauth_register.php 就是負責分發消費方 key 和金鑰的控制器檔。

 

 

$user_id = 1;

// 下面的內容應該來自使用者填寫的表單

$consumer = array(

// 下面兩個是必須的

'requester_name' => 'John Doe',

'requester_email' => 'john@example.com',

// 下面的是可選的

'callback_uri' => 'HTTP://www.myconsumersite.com/oauth_callback',

'application_uri' => 'HTTP://www.myconsumersite.com/',

'application_title' => 'John Doe\'s consumer site',

'application_descr' => 'Make nice graphs of all your data',

'application_notes' => 'Bladibla',

'application_type' => 'website',

'application_commercial' => 0

);

// 註冊消費方

$store = OAuthStore::instance();

$key = $store->updateConsumer($consumer, $user_id);

// 從資料儲存體獲得完整的消費方資訊

$consumer = $store->getConsumer($key);

// 消費方使用者將需要 key 和 secret

$consumer_id = $consumer['id'];

$consumer_key = $consumer['consumer_key'];

$consumer_secret = $consumer['consumer_secret'];

 

如果你想要更新之前註冊的消費方身份,提供消費方id,key 和 secret 。key 和 secret 在更新操作完成之前不會進行改變。

 

你還可以請求一個特定使用者註冊的所有消費方清單:

 

$user_id = 1;

// 取得這個使用者註冊的全部消費方清單

$store = OAuthStore::instance();

$list = $store->listConsumers($user_id);

 

request_token.php 這個控制器檔負責返回請求 token(未認證的 request token)。當消費方獲取了 key 和 secret 之後,它就可以向伺服器端請求未認證的 request token 了:

$server = new OAuthServer();

$token = $server->requestToken();

exit();

authorize.php 控制器檔負責認證一個使用者請求 token。這個控制器負責詢問使用者是否允許消費方訪問他的帳戶。如果允許,那麼消費方將可以使用 request token 換取 access token。必須要保證使用者在訪問下面的代碼之前是登錄狀態。OAuthServer 伺服器使用 SESSION 存儲一些 OAuth 狀態資訊,所以必須要開啟seesion會話(要麼是 session_start 函數,要麼就是自動開啟)。

$user_id = 1;

// 取得OAuth儲存體和OAtuh伺服器物件

$store = OAuthStore::instance();

$server = new OAuthServer();

try

{

// 檢查當前請求中是否包含合法的request token

// 返回一個包含消費方key, 消費方secret, token, token secret 和 token 類型的陣列.

$rs = $server->authorizeVerify();

if ($_SERVER['REQUEST_METHOD'] == 'POST')

{

// 檢查使用者是否點擊了 'allow' 按鈕或者其他你指定的按鈕)

$authorized = array_key_exists('allow', $_POST);

// 設置 request token 的認證狀態(已認證或者是未認證)

// 當包含 oauth_callback 回檔的時候,這些將傳給消費方

$server->authorizeFinish($authorized, $user_id);

// 沒有 oauth_callback 回檔, 顯示認證結果

// ** 你的代碼 **

}

}

catch (OAuthException $e)

{

// 沒有需要認證的 request token, 顯示一個可以輸入 request token 的頁面

// ** 你的代碼 **

}

 

access_token.php 控制器檔負責將認證的request token換成access token。access token 可以被用來為請求籤名。

 

$server = new OAuthServer();

$server->accessToken();

 

arrow
arrow
    全站熱搜

    戮克 發表在 痞客邦 留言(0) 人氣()