【PHP】基于ThinkPHP框架搭建OAuth2.0服务

这几天一直在搞OAuth2.0的东西,写SDK啥的,为了更加深入的了解服务端的OAuth验证机制,就自己动手搭了个php下OAuth的环境,并且将它移植到了自己比较熟的tp框架里。

废话不少说,开动。

 

其实网上是有OAuth2.0的php版本的。

你可以在http://code.google.com/p/oauth2-php/ 找到源代码,上面实现了PDO和MongoDB的数据模式。这里我也是基于这些代码在TP中进行整合的。

 

好,这里我们可以把下载下来的包解压,把Lib下的OAuth.inc改名为OAuth2.class.php后放到tp核心包下的目录下:

 

Tree代码  收藏代码

  1. /Extend/Library/ORG/OAuth/OAuth2.class.php  

 

接下来我们要继承这个类;

在这个目录下新建一个ThinkOAuth2.class.php文件:

 

Php代码  收藏代码

  1. /** 
  2.  * @category ORG 
  3.  * @package ORG 
  4.  * @author Leyteris 
  5.  * @version 2012.3.16 
  6.  */  
  7.   
  8. // OAUTH2_DB_DSN  数据库连接DSN  
  9. // OAUTH2_CODES_TABLE 服务器表名称  
  10. // OAUTH2_CLIENTS_TABLE 客户端表名称  
  11. // OAUTH2_TOKEN_TABLE 验证码表名称  
  12.   
  13. import("ORG.OAuth.OAuth2");  
  14.   
  15. class ThinkOAuth2 extends OAuth2 {  
  16.   
  17.     private $db;  
  18.     private $table;  
  19.   
  20.     /** 
  21.      * 构造 
  22.      */  
  23.     public function __construct() {  
  24.         parent::__construct();  
  25.         $this -> db = Db::getInstance(C('OAUTH2_DB_DSN'));  
  26.         $this -> table = array(  
  27.             'auth_codes'=>C('OAUTH2_CODES_TABLE'),  
  28.             'clients'=>C('OAUTH2_CLIENTS_TABLE'),  
  29.             'tokens'=>C('OAUTH2_TOKEN_TABLE')  
  30.         );  
  31.     }  
  32.   
  33.     /** 
  34.      * 析构 
  35.      */  
  36.     function __destruct() {  
  37.         $this->db = NULL; // Release db connection  
  38.     }  
  39.   
  40.     private function handleException($e) {  
  41.         echo "Database error: " . $e->getMessage();  
  42.         exit;  
  43.     }  
  44.   
  45.     /** 
  46.      *  
  47.      * 增加client 
  48.      * @param string $client_id 
  49.      * @param string $client_secret 
  50.      * @param string $redirect_uri 
  51.      */  
  52.     public function addClient($client_id, $client_secret, $redirect_uri) {  
  53.           
  54.         $time = time();  
  55.         $sql = "INSERT INTO {$this -> table['clients']} ".  
  56.             "(client_id, client_secret, redirect_uri, create_time) VALUES (\"{$client_id}\", \"{$client_secret}\", \"{$redirect_uri}\",\"{$time}\")";  
  57.           
  58.         $this -> db -> execute($sql);  
  59.           
  60.     }  
  61.   
  62.     /** 
  63.      * Implements OAuth2::checkClientCredentials() 
  64.      * @see OAuth2::checkClientCredentials() 
  65.      */  
  66.     protected function checkClientCredentials($client_id, $client_secret = NULL) {  
  67.           
  68.         $sql = "SELECT client_secret FROM {$this -> table['clients']} ".  
  69.             "WHERE client_id = \"{$client_id}\"";  
  70.           
  71.         $result = $this -> db -> query($sql);  
  72.         if ($client_secret === NULL) {  
  73.             return $result !== FALSE;  
  74.         }  
  75.           
  76.         //Log::write("checkClientCredentials : ".$result);  
  77.         //Log::write("checkClientCredentials : ".$result[0]);  
  78.         //Log::write("checkClientCredentials : ".$result[0]["client_secret"]);  
  79.           
  80.         return $result[0]["client_secret"] == $client_secret;  
  81.           
  82.     }  
  83.   
  84.     /** 
  85.      * Implements OAuth2::getRedirectUri(). 
  86.      * @see OAuth2::getRedirectUri() 
  87.      */  
  88.     protected function getRedirectUri($client_id) {  
  89.           
  90.         $sql = "SELECT redirect_uri FROM {$this -> table['clients']} ".  
  91.             "WHERE client_id = \"{$client_id}\"";  
  92.           
  93.         $result = $this -> db -> query($sql);  
  94.           
  95.         if ($result === FALSE) {  
  96.             return FALSE;  
  97.         }  
  98.           
  99.         //Log::write("getRedirectUri : ".$result);  
  100.         //Log::write("getRedirectUri : ".$result[0]);  
  101.         //Log::write("getRedirectUri : ".$result[0]["redirect_uri"]);  
  102.           
  103.         return isset($result[0]["redirect_uri"]) && $result[0]["redirect_uri"] ? $result[0]["redirect_uri"] : NULL;  
  104.           
  105.     }  
  106.   
  107.     /** 
  108.      * Implements OAuth2::getAccessToken(). 
  109.      * @see OAuth2::getAccessToken() 
  110.      */  
  111.     protected function getAccessToken($access_token) {  
  112.           
  113.         $sql = "SELECT client_id, expires, scope FROM {$this -> table['tokens']} ".  
  114.             "WHERE access_token = \"{$access_token}\"";  
  115.           
  116.         $result = $this -> db -> query($sql);  
  117.           
  118.         //Log::write("getAccessToken : ".$result);  
  119.         //Log::write("getAccessToken : ".$result[0]);  
  120.           
  121.         return $result !== FALSE ? $result : NULL;  
  122.           
  123.     }  
  124.   
  125.     /** 
  126.      * Implements OAuth2::setAccessToken(). 
  127.      * @see OAuth2::setAccessToken() 
  128.      */  
  129.     protected function setAccessToken($access_token, $client_id, $expires, $scope = NULL) {  
  130.           
  131.         $sql = "INSERT INTO {$this -> table['tokens']} ".  
  132.             "(access_token, client_id, expires, scope) ".  
  133.             "VALUES (\"{$access_token}\", \"{$client_id}\", \"{$expires}\", \"{$scope}\")";  
  134.           
  135.         $this -> db -> execute($sql);  
  136.           
  137.     }  
  138.   
  139.     /** 
  140.      * Overrides OAuth2::getSupportedGrantTypes(). 
  141.      * @see OAuth2::getSupportedGrantTypes() 
  142.      */  
  143.     protected function getSupportedGrantTypes() {  
  144.         return array(  
  145.             OAUTH2_GRANT_TYPE_AUTH_CODE  
  146.         );  
  147.     }  
  148.   
  149.     /** 
  150.      * Overrides OAuth2::getAuthCode(). 
  151.      * @see OAuth2::getAuthCode() 
  152.      */  
  153.     protected function getAuthCode($code) {  
  154.           
  155.         $sql = "SELECT code, client_id, redirect_uri, expires, scope ".  
  156.             "FROM {$this -> table['auth_codes']} WHERE code = \"{$code}\"";  
  157.           
  158.         $result = $this -> db -> query($sql);  
  159.           
  160.         //Log::write("getAuthcode : ".$result);  
  161.         //Log::write("getAuthcode : ".$result[0]);  
  162.         //Log::write("getAuthcode : ".$result[0]["code"]);  
  163.           
  164.         return $result !== FALSE ? $result[0] : NULL;  
  165.   
  166.     }  
  167.   
  168.     /** 
  169.      * Overrides OAuth2::setAuthCode(). 
  170.      * @see OAuth2::setAuthCode() 
  171.      */  
  172.     protected function setAuthCode($code, $client_id, $redirect_uri, $expires, $scope = NULL) {  
  173.           
  174.         $time = time();  
  175.         $sql = "INSERT INTO {$this -> table['auth_codes']} ".  
  176.             "(code, client_id, redirect_uri, expires, scope) ".  
  177.             "VALUES (\"${code}\", \"${client_id}\", \"${redirect_uri}\", \"${expires}\", \"${scope}\")";  
  178.           
  179.         $result = $this -> db -> execute($sql);  
  180.   }  
  181.     
  182.   /** 
  183.    * Overrides OAuth2::checkUserCredentials(). 
  184.    * @see OAuth2::checkUserCredentials() 
  185.    */  
  186.   protected function checkUserCredentials($client_id, $username, $password){  
  187.     return TRUE;  
  188.   }  
  189. }  

 

 在这里我们需要创建数据库:

 

Sql代码  收藏代码

  1. CREATE TABLE `oauth_client` (  
  2.   `id` bigint(20) NOT NULL auto_increment,  
  3.   `client_id` varchar(32) NOT NULL,  
  4.   `client_secret` varchar(32) NOT NULL,  
  5.   `redirect_uri` varchar(200) NOT NULL,  
  6.   `create_time` int(20) default NULL,  
  7.   PRIMARY KEY  (`id`)  
  8. ) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;  
  9.   
  10. CREATE TABLE `oauth_code` (  
  11.   `id` bigint(20) NOT NULL auto_increment,  
  12.   `client_id` varchar(32) NOT NULL,  
  13.   `user_id` varchar(32) NOT NULL,  
  14.   `code` varchar(40) NOT NULL,  
  15.   `redirect_uri` varchar(200) NOT NULL,  
  16.   `expires` int(11) NOT NULL,  
  17.   `scope` varchar(250) default NULL,  
  18.   PRIMARY KEY  (`id`)  
  19. ) ENGINE=MyISAM DEFAULT CHARSET=utf8;  
  20.   
  21. CREATE TABLE `oauth_token` (  
  22.   `id` bigint(20) NOT NULL auto_increment,  
  23.   `client_id` varchar(32) NOT NULL,  
  24.   `user_id` varchar(32) NOT NULL,  
  25.   `access_token` varchar(40) NOT NULL,  
  26.   `refresh_token` varchar(40) NOT NULL,  
  27.   `expires` int(11) NOT NULL,  
  28.   `scope` varchar(200) default NULL,  
  29.   PRIMARY KEY  (`id`)  
  30. ) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;  

 

 

上面的数据库表名可以自己随便定;但是要在config.php配置表名:

 

Php代码  收藏代码

  1. 'OAUTH2_CODES_TABLE'=>'oauth_code',  
  2. 'OAUTH2_CLIENTS_TABLE'=>'oauth_client',  
  3. 'OAUTH2_TOKEN_TABLE'=>'oauth_token',  

 

如果OAuth的服务器不是当前服务器,那就要指定下DSN地址了:

 

 

Php代码  收藏代码

  1. 'OAUTH2_DB_DSN'=>'mysql://root:mima@l:3306/database'  

 

 

好了,大致的核心库代码就是如此。接下来要使用它

 

我们创建一个OAuth的Action负责OAuth2的一些验证(OauthAction.class.php)

 

Php代码  收藏代码

  1. import("ORG.OAuth.ThinkOAuth2");  
  2.   
  3. class OauthAction extends Action {  
  4.       
  5.     private $oauth = NULL;  
  6.   
  7.     function _initialize(){  
  8.           
  9.         header("Content-Type: application/json");  
  10.         "white-space: pre;">    header("Cache-Control: no-store");  
  11.         $this -> oauth = new ThinkOAuth2();  
  12.   
  13.     }  
  14.       
  15.     public function index(){  
  16.           
  17.         header("Content-Type:application/json; charset=utf-8");  
  18.         $this -> ajaxReturn(null, 'oauth-server-start', 1, 'json');  
  19.           
  20.     }  
  21.       
  22.     public function access_token() {  
  23.           
  24.         $this -> oauth -> grantAccessToken();  
  25.   
  26.     }  
  27.       
  28.     //权限验证  
  29.     public function authorize() {  
  30.           
  31.         if ($_POST) {  
  32.             $this -> oauth -> finishClientAuthorization($_POST["accept"] == "Yep", $_POST);  
  33.             return;  
  34.         }  
  35.           
  36.         ///表单准备  
  37.         $auth_params = $this -> oauth -> getAuthorizeParams();  
  38.         $this -> assign("params", $auth_params);  
  39.         $this->display();  
  40.   
  41.     }  
  42.       
  43.     public function addclient() {  
  44.           
  45.         if ($_POST && isset($_POST["client_id"]) &&  
  46.          isset($_POST["client_secret"]) &&   
  47.             isset($_POST["redirect_uri"])) {  
  48.                   
  49.             $this -> oauth -> addClient($_POST["client_id"], $_POST["client_secret"], $_POST["redirect_uri"]);  
  50.             return;  
  51.         }  
  52.           
  53.         $this->display();  
  54.     }  
  55. }  

 

 

这里我们创建了一个私有的oauth对象并在初始化的时候去init它。

 

以上的代码在password那个部分没有做验证,第三种模式需要把ThinkOAuth类中的checkUserCredentials方法进行重写。

 

继续我们写一个受限资源代码。我们这里没有用AOP进行拦截,所以我准备直接用一个基类来模拟拦截。

 

 

Php代码  收藏代码

  1. import("ORG.OAuth.ThinkOAuth2");  
  2.   
  3. class BaseAction extends Action {  
  4.       
  5.     protected $oauth = NULL;  
  6.   
  7.     function _initialize(){  
  8.           
  9.         $this -> oauth = new ThinkOAuth2();  
  10.   
  11.     }  
  12.       
  13.     public function index(){  
  14.           
  15.         if(!$this -> oauth -> verifyAccessToken()){  
  16.             $this -> ajaxReturn(null, 'no,no,no', 0, 'json');  
  17.             exit();  
  18.         }  
  19.         $this -> ajaxReturn(null, 'oauth-server', 1, 'json');  
  20.           
  21.     }  
  22.       
  23. }  

 

 接下来直接用一个UserAction来继承它达到受限的目的,如下:

 

 

Php代码  收藏代码

  1. class UserAction extends BaseAction {  
  2.       
  3.     public function index(){  
  4.   
  5.         if(!$this -> oauth -> verifyAccessToken()){  
  6.             $this -> ajaxReturn(null, 'no,no,no', 0, 'json');  
  7.         }  
  8.         $this -> ajaxReturn(null, 'oauth-server', 1, 'json');  
  9.           
  10.     }  
  11.       
  12. }  

 

 

 

 

最后说明一点,为什么要把user_id耦合进OAuth的表呢?因为我们有时候需要从access_token返查user_id,上面的表就能解决这个问题,但其实还有一种方式是在对于access_token生成的时候自动包含user_id再进行加密,在解码的时候从access_token直接取出user_id就可以了。这里关于user_id和密码验证的都没有去实现,需要后期继承ThinkOAuth2类或者修改checkUserCredentials方法才能实现的。 另外这套东西用在REST模式下我认为更好!

你可能感兴趣的:(PHP开发框架)