737
汽車大全
【PHP】基於ThinkPHP框架搭建OAuth2.0服務
from:https://leyteris.iteye.com/blog/1483403
這幾天一直在搞OAuth2.0的東西,寫SDK啥的,為了更加深入的了解服務端的OAuth驗證機製,就自己動手搭了個php下OAuth的環境,並且將它移植到了自己比較熟的tp框架裏。
廢話不少說,開動。
其實網上是有OAuth2.0的php版本的。
你可以在https://code.google.com/p/oauth2-php/ 找到源代碼,上麵實現了PDO和MongoDB的數據模式。這裏我也是基於這些代碼在TP中進行整合的。
好,這裏我們可以把下載下來的包解壓,把Lib下的OAuth.inc改名為OAuth2.class.php後放到tp核心包下的目錄下:
接下來我們要繼承這個類;
在這個目錄下新建一個ThinkOAuth2.class.php文件:
- <?php
- /**
- * @category ORG
- * @package ORG
- * @author Leyteris
- * @version 2012.3.16
- */
- // OAUTH2_DB_DSN 數據庫連接DSN
- // OAUTH2_CODES_TABLE 服務器表名稱
- // OAUTH2_CLIENTS_TABLE 客戶端表名稱
- // OAUTH2_TOKEN_TABLE 驗證碼表名稱
- import("ORG.OAuth.OAuth2");
- class ThinkOAuth2 extends OAuth2 {
- private $db;
- private $table;
- /**
- * 構造
- */
- public function __construct() {
- parent::__construct();
- $this -> db = Db::getInstance(C('OAUTH2_DB_DSN'));
- $this -> table = array(
- 'auth_codes'=>C('OAUTH2_CODES_TABLE'),
- 'clients'=>C('OAUTH2_CLIENTS_TABLE'),
- 'tokens'=>C('OAUTH2_TOKEN_TABLE')
- );
- }
- /**
- * 析構
- */
- function __destruct() {
- $this->db = NULL; // Release db connection
- }
- private function handleException($e) {
- echo "Database error: " . $e->getMessage();
- exit;
- }
- /**
- *
- * 增加client
- * @param string $client_id
- * @param string $client_secret
- * @param string $redirect_uri
- */
- public function addClient($client_id, $client_secret, $redirect_uri) {
- $time = time();
- $sql = "INSERT INTO {$this -> table['clients']} ".
- "(client_id, client_secret, redirect_uri, create_time) VALUES (\"{$client_id}\", \"{$client_secret}\", \"{$redirect_uri}\",\"{$time}\")";
- $this -> db -> execute($sql);
- }
- /**
- * Implements OAuth2::checkClientCredentials()
- * @see OAuth2::checkClientCredentials()
- */
- protected function checkClientCredentials($client_id, $client_secret = NULL) {
- $sql = "SELECT client_secret FROM {$this -> table['clients']} ".
- "WHERE client_id = \"{$client_id}\"";
- $result = $this -> db -> query($sql);
- if ($client_secret === NULL) {
- return $result !== FALSE;
- }
- //Log::write("checkClientCredentials : ".$result);
- //Log::write("checkClientCredentials : ".$result[0]);
- //Log::write("checkClientCredentials : ".$result[0]["client_secret"]);
- return $result[0]["client_secret"] == $client_secret;
- }
- /**
- * Implements OAuth2::getRedirectUri().
- * @see OAuth2::getRedirectUri()
- */
- protected function getRedirectUri($client_id) {
- $sql = "SELECT redirect_uri FROM {$this -> table['clients']} ".
- "WHERE client_id = \"{$client_id}\"";
- $result = $this -> db -> query($sql);
- if ($result === FALSE) {
- return FALSE;
- }
- //Log::write("getRedirectUri : ".$result);
- //Log::write("getRedirectUri : ".$result[0]);
- //Log::write("getRedirectUri : ".$result[0]["redirect_uri"]);
- return isset($result[0]["redirect_uri"]) && $result[0]["redirect_uri"] ? $result[0]["redirect_uri"] : NULL;
- }
- /**
- * Implements OAuth2::getAccessToken().
- * @see OAuth2::getAccessToken()
- */
- protected function getAccessToken($access_token) {
- $sql = "SELECT client_id, expires, scope FROM {$this -> table['tokens']} ".
- "WHERE access_token = \"{$access_token}\"";
- $result = $this -> db -> query($sql);
- //Log::write("getAccessToken : ".$result);
- //Log::write("getAccessToken : ".$result[0]);
- return $result !== FALSE ? $result : NULL;
- }
- /**
- * Implements OAuth2::setAccessToken().
- * @see OAuth2::setAccessToken()
- */
- protected function setAccessToken($access_token, $client_id, $expires, $scope = NULL) {
- $sql = "INSERT INTO {$this -> table['tokens']} ".
- "(access_token, client_id, expires, scope) ".
- "VALUES (\"{$access_token}\", \"{$client_id}\", \"{$expires}\", \"{$scope}\")";
- $this -> db -> execute($sql);
- }
- /**
- * Overrides OAuth2::getSupportedGrantTypes().
- * @see OAuth2::getSupportedGrantTypes()
- */
- protected function getSupportedGrantTypes() {
- return array(
- OAUTH2_GRANT_TYPE_AUTH_CODE
- );
- }
- /**
- * Overrides OAuth2::getAuthCode().
- * @see OAuth2::getAuthCode()
- */
- protected function getAuthCode($code) {
- $sql = "SELECT code, client_id, redirect_uri, expires, scope ".
- "FROM {$this -> table['auth_codes']} WHERE code = \"{$code}\"";
- $result = $this -> db -> query($sql);
- //Log::write("getAuthcode : ".$result);
- //Log::write("getAuthcode : ".$result[0]);
- //Log::write("getAuthcode : ".$result[0]["code"]);
- return $result !== FALSE ? $result[0] : NULL;
- }
- /**
- * Overrides OAuth2::setAuthCode().
- * @see OAuth2::setAuthCode()
- */
- protected function setAuthCode($code, $client_id, $redirect_uri, $expires, $scope = NULL) {
- $time = time();
- $sql = "INSERT INTO {$this -> table['auth_codes']} ".
- "(code, client_id, redirect_uri, expires, scope) ".
- "VALUES (\"${code}\", \"${client_id}\", \"${redirect_uri}\", \"${expires}\", \"${scope}\")";
- $result = $this -> db -> execute($sql);
- }
- /**
- * Overrides OAuth2::checkUserCredentials().
- * @see OAuth2::checkUserCredentials()
- */
- protected function checkUserCredentials($client_id, $username, $password){
- return TRUE;
- }
- }
在這裏我們需要創建數據庫:
- CREATE TABLE `oauth_client` (
- `id` bigint(20) NOT NULL auto_increment,
- `client_id` varchar(32) NOT NULL,
- `client_secret` varchar(32) NOT NULL,
- `redirect_uri` varchar(200) NOT NULL,
- `create_time` int(20) default NULL,
- PRIMARY KEY (`id`)
- ) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
- CREATE TABLE `oauth_code` (
- `id` bigint(20) NOT NULL auto_increment,
- `client_id` varchar(32) NOT NULL,
- `user_id` varchar(32) NOT NULL,
- `code` varchar(40) NOT NULL,
- `redirect_uri` varchar(200) NOT NULL,
- `expires` int(11) NOT NULL,
- `scope` varchar(250) default NULL,
- PRIMARY KEY (`id`)
- ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
- CREATE TABLE `oauth_token` (
- `id` bigint(20) NOT NULL auto_increment,
- `client_id` varchar(32) NOT NULL,
- `user_id` varchar(32) NOT NULL,
- `access_token` varchar(40) NOT NULL,
- `refresh_token` varchar(40) NOT NULL,
- `expires` int(11) NOT NULL,
- `scope` varchar(200) default NULL,
- PRIMARY KEY (`id`)
- ) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
上麵的數據庫表名可以自己隨便定;但是要在config.php配置表名:
- 'OAUTH2_CODES_TABLE'=>'oauth_code',
- 'OAUTH2_CLIENTS_TABLE'=>'oauth_client',
- 'OAUTH2_TOKEN_TABLE'=>'oauth_token',
如果OAuth的服務器不是當前服務器,那就要指定下DSN地址了:
好了,大致的核心庫代碼就是如此。接下來要使用它
我們創建一個OAuth的Action負責OAuth2的一些驗證(OauthAction.class.php)
- import("ORG.OAuth.ThinkOAuth2");
- class OauthAction extends Action {
- private $oauth = NULL;
- function _initialize(){
- header("Content-Type: application/json");
- <span style="white-space: pre;"> </span>header("Cache-Control: no-store");
- $this -> oauth = new ThinkOAuth2();
- }
- public function index(){
- header("Content-Type:application/json; charset=utf-8");
- $this -> ajaxReturn(null, 'oauth-server-start', 1, 'json');
- }
- public function access_token() {
- $this -> oauth -> grantAccessToken();
- }
- //權限驗證
- public function authorize() {
- if ($_POST) {
- $this -> oauth -> finishClientAuthorization($_POST["accept"] == "Yep", $_POST);
- return;
- }
- ///表單準備
- $auth_params = $this -> oauth -> getAuthorizeParams();
- $this -> assign("params", $auth_params);
- $this->display();
- }
- public function addclient() {
- if ($_POST && isset($_POST["client_id"]) &&
- isset($_POST["client_secret"]) &&
- isset($_POST["redirect_uri"])) {
- $this -> oauth -> addClient($_POST["client_id"], $_POST["client_secret"], $_POST["redirect_uri"]);
- return;
- }
- $this->display();
- }
- }
這裏我們創建了一個私有的oauth對象並在初始化的時候去init它。
以上的代碼在password那個部分沒有做驗證,第三種模式需要把ThinkOAuth類中的checkUserCredentials方法進行重寫。
繼續我們寫一個受限資源代碼。我們這裏沒有用AOP進行攔截,所以我準備直接用一個基類來模擬攔截。
- import("ORG.OAuth.ThinkOAuth2");
- class BaseAction extends Action {
- protected $oauth = NULL;
- function _initialize(){
- $this -> oauth = new ThinkOAuth2();
- }
- public function index(){
- if(!$this -> oauth -> verifyAccessToken()){
- $this -> ajaxReturn(null, 'no,no,no', 0, 'json');
- exit();
- }
- $this -> ajaxReturn(null, 'oauth-server', 1, 'json');
- }
- }
接下來直接用一個UserAction來繼承它達到受限的目的,如下:
- class UserAction extends BaseAction {
- public function index(){
- if(!$this -> oauth -> verifyAccessToken()){
- $this -> ajaxReturn(null, 'no,no,no', 0, 'json');
- }
- $this -> ajaxReturn(null, 'oauth-server', 1, 'json');
- }
- }
最後說明一點,為什麼要把user_id耦合進OAuth的表呢?因為我們有時候需要從access_token返查user_id,上麵的表就能解決這個問題,但其實還有一種方式是在對於access_token生成的時候自動包含user_id再進行加密,在解碼的時候從access_token直接取出user_id就可以了。這裏關於user_id和密碼驗證的都沒有去實現,需要後期繼承ThinkOAuth2類或者修改checkUserCredentials方法才能實現的。 另外這套東西用在REST模式下我認為更好!
最後更新:2017-04-03 05:39:37