搭建OAuth2.0

vboy1010發表於2019-02-16

前奏

系統:Ubuntu
語言:PHP7
框架:YAF
OAuth2.0:bshaffer/oauth2-server-php

OAuth2.0 有很多開原始碼庫

Github 排名前兩位

thephpleague/oauth2-server
bshaffer/oauth2-server-php

本文使用的是第二個:bshaffer。原因:使用簡單,可以很快上手,文件齊全,功能完善。

wiki: https://bshaffer.github.io/oa…
github: https://github.com/bshaffer/o…

引入 OAuth2.0 的 Server 端原始碼

編輯 Composer.json 檔案

{
    "require": {
        "bshaffer/oauth2-server-php" : "v1.10.0"
    }
}

yaf框架結構

├── application
│   └── modules
│       └── User
│           ├── controllers
│           │   └── Oauth.php
│           └── views
│               └── oauth
│                   ├── authorize.php
│                   ├── auth.php
│                   ├── index.php
│                   └── resource.php
├── Bootstrap.php
├── cli
├── composer.json
├── composer.lock
├── composer.phar
├── conf
├── docs
├── public
└── vendor

Yaf 框架中,在 Bootstrap.php 檔案中自動載入 OAuth2.0

public function _initLoader(Yaf_Dispatcher $dispatcher)
{
    include(APP_PATH . `/vendor/autoload.php`);
}

新建一個 Controller 檔案:Oauth.php,在裡面建立幾個 Action

AuthorizeAction()   服務端:提供授權
TokenAction()       服務端:提供Token
ResourceAction()    服務端:提供資源
IndexAction()       客戶端:模擬第三方接入
_server()           服務端:初始化伺服器相關,如:儲存,這裡採用mysql

名稱空間

use OAuth2Server;
use OAuth2StoragePdo;
use OAuth2GrantTypeAuthorizationCode;
use OAuth2GrantTypeClientCredentials;
use OAuth2GrantTypeUserCredentials;
use OAuth2Request;
use OAuth2Response;

_server() 函式程式碼

private function _server()
{
    $dbParams = array(
        `dsn`      => `mysql:host=127.0.0.1;port=3306;dbname=oauth;charset=utf8;`,
        `username` => `root`,
        `password` => `123456`,
    );

    // $dsn is the Data Source Name for your database, for exmaple "mysql:dbname=my_oauth2_db;host=localhost"
    $storage = new Pdo($dbParams);

    // Pass a storage object or array of storage objects to the OAuth2 server class
    $server = new Server($storage);

    // Add the "Client Credentials" grant type (it is the simplest of the grant types)
    $server->addGrantType(new ClientCredentials($storage));

    // Add the "Authorization Code" grant type (this is where the oauth magic happens)
    $server->addGrantType(new AuthorizationCode($storage));

    return $server;
}

IndexAction() 程式碼

public function indexAction()
{
    $uri   = $_SERVER[`QUERY_STRING`];
    $code  = substr($uri, strpos($uri, `code=`)+5, 40);
    $state = substr($uri, strpos($uri, `state=`)+6);

    if ($code) {
        $params = array(
            `code`          => $code,
            `state`         => $state,
            `client_id`     => `client_id`,
            `client_secret` => `client_secret`,
            `grant_type`    => `authorization_code`,
            `scope`         => `basic`,
            `redirect_uri`  => `http://yourhost/user/oauth/index`,
        );
        
        $url = `http://yourhost/user/oauth/token`;

        $result = $this->httpPost($url, $params);
        $result = json_decode($result, true);

        //寫入Session,便於測試
        Yaf_Session::getInstance()->set(`access_token`,$result[`access_token`]);
        return false;
    } else {
        //客戶端請求授權之前,頁面中展示一個連結,使用者點後,可以跳轉至服務端的授權頁面。
        $this->getView()->assign(`data`, array())->render(`oauth/index.php`);
    }
}

對應的模板程式碼:oauth/index.php

<div>
<a href="http://yourhost/User/Oauth/Authorize?response_type=code&client_id=client_id&redirect_uri=http://yourhost/user/oauth/index&scope=basic&state=xxx">click here</a>
</div>

AuthorizeAction() 程式碼

/**
 * 展示授權頁面,使用者可以點選同意進行授權
 */
public function AuthorizeAction()
{
    $request = Request::createFromGlobals();
    $is_authorized = $request->request(`is_authorized`) ? true : false;

    //判斷使用者是否同意授權
    if ($is_authorized) {

        $response = new Response();
        $server = $this->_server();

        // validate the authorize request
        if (!$server->validateAuthorizeRequest($request, $response)) {
            $response->send();
            die;
        }

        $server->handleAuthorizeRequest($request, $response, $is_authorized)->send();

        return false;
    }

    //將請求授權中帶來的各個引數,寫入授權頁中的變數,用以授權表單POST提交。
    $renderData = $_GET;
    $this->getView()->assign(`data`, $renderData)->render(`oauth/authorize.php`);
}

使用者授權頁面:oauth/authorize.php

<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>直達天庭 - 南天門</title>
    <link type="text/css" href="http://api.weibo.com/oauth2/css/oauthV3/oauth_web.css?version=20140625" rel="stylesheet"/>
    </head>
<body class="WB_UIbody WB_widgets">
<div class="WB_xline1 oauth_xline" id="outer">
    <div class="oauth_wrap">
        <div class="oauth_header clearfix">
            <h1 class="WB_logo" title="微博"><a href="http://yourhost">天庭</a></h1>
            <p class="login_account">
                <span class="account_name">哪吒</span>
                <span class="vline">|</span><a href="http://yourhost/user/oauth/list" target="_blank">我的應用</a></span>
            </p>
        </div>
        <!-- 帶頭像  -->
        <div class="WB_panel oauth_main">
            <form id="authZForm" name="authZForm" action="/user/oauth/authorize" method="post" node-type="form">
                <div class="oauth_content">
                    <div class="oauth_main_content clearfix">
                        <div class="app_info clearfix">
                            <div class="app_info_main clearfix">
                                <div class="app_icon">
                                    <img class="app_img"
                                         src="https://upload.api.weibo.com/square/aa397b7fgw1f556jk3z3bj20280283yc.jpg"
                                         alt="app">
                                </div>
                                <div class="app_intro">
                                    <h3><a href="http://app.weibo.com/t/feed/324Mrt" target="_blank">人間</a></h3>
                                    <div class="app_des">凡人</div>
                                </div>
                            </div>
                            <div class="app_info_plus">
                                <div class="plus_tit">
                                    http://app.weibo.com/t/feed/324Mrt<br/>有 1 個你關注的人連線
                                    </div>
                                <div class="app_user_list">
                                    <ul class="clearfix">
                                        </ul>
                                </div>
                            </div>
                        </div>
                        <div class="oauth_info clearfix">
                            <div class="oauth_list">
                                <div class="list_tit">將允許<a href="http://app.weibo.com/t/feed/324Mrt" target="_blank">人間</a>進行以下操作:
                                </div>
                                <ul class="do_list">
                                    <li>
                                        <i class="icon_user"></i>獲得你的個人資訊,好友關係
                                    </li>
                                    <li>
                                        <i class="icon_rss"></i>分享內容到你的微博
                                    </li>
                                    <li>
                                        <i class="icon_comm"></i>獲得你的評論
                                    </li>
                                </ul>
                            </div>
                            </div>
                    </div>
                    <!-- 登入 -->
                    <div class="oauth_login_box01 clearfix">

                        <input type="hidden" name="scope" id="scope" value="<?php echo $data[`scope`];?>"/>
                        <input type="hidden" name="response_type" value="<?php echo $data[`response_type`];?>"/>
                        <input type="hidden" name="redirect_uri" value="<?php echo $data[`redirect_uri`];?>"/>
                        <input type="hidden" name="client_id" value="<?php echo $data[`client_id`];?>"/>
                        <input type="hidden" name="state" value="<?php echo $data[`state`];?>"/>
                        <input type="hidden" name="is_authorized" value="1"/>
                        <input type="hidden" name="grant_type" value="<?php echo $data[`grant_type`];?>"/>
                        <!-- </form> -->

                        <div class="oauth_login_submit">
                            <p class="oauth_formbtn"><a node-type="submit" href="#" onclick="return false;"
                                                        action-type="submit"
                                                        class="WB_btn_link formbtn_01"></a><a
                                    node-type="cancel" href="javascript:;" action-type="cancel"
                                    class="WB_btn_cancel"></a></p>
                        </div>
                    </div>
                </div>
            </form>
            <!-- /登入 -->
        </div>
        </div>
</div>
</body>
</html>
<script type="text/javascript" src="https://code.jquery.com/jquery-1.11.3.js"></script>
<script type="text/javascript">
$(document).ready(function(){
    $(".WB_btn_link").click(function(){
        $("#authZForm").submit();
    });
})

</script>

TokenAction()

/**
 * 返回JSON結構資料
 * {
        access_token: "977b1077556e9b23ff07ef7606a5eaf947f27d41",
        expires_in: 3600,
        token_type: "Bearer",
        scope: "basic",
        refresh_token: "d2367887bdd743121adfe5fda5083064439f1cb1"
    }
 */
public function TokenAction()
{
    $server = $this->_server();
    $server->handleTokenRequest(Request::createFromGlobals())->send();
    return false;
}

ResourceAction() 程式碼

public function ResourceAction()
{
    $server = $this->_server();

    //獲取授權使用者Session中儲存的access_token,用access_token可以請求許可權範圍內的所有介面
    $_POST[`access_token`] = Yaf_Session::getInstance()->get(`access_token`);
    
    // Handle a request to a resource and authenticate the access token
    if (!$server->verifyResourceRequest(Request::createFromGlobals())) {
        $server->getResponse()->send();
        die;
    }
    echo json_encode(array(`success` => true, `message` => `You accessed my APIs!`));
    return false;
}

請求使用者資源:/user/oauth/resource

{
    success: true,
    message: "You accessed my APIs!"
}

實現思路:

  • URL請求:/user/oauth/index,Client端

    對應的模板裡放置一個連結,用以跳轉至服務端的授權認證頁面/user/oauth/authorize。授權成功後,服務端會將對應的code和state引數,附在回撥的redirect_uri裡,即:/user/oauth/index?code=fcd6a9589e7ab43398e4e5349b23846babc79fab&state=xxx,在indexAction中解析出回撥的code,用以請求介面/user/oauth/token,來交換access_token。

  • URL請求:/user/oauth/authorize,Server端
    對應的模板裡是告知使用者,即將授予的許可權列表,以及是否允許授權的按鈕。
  • URL請求:/user/oauth/token,Server端
    獲取access_token。
  • URL請求:/user/oauth/resource,Server端
    獲取使用者資源

LAST:

  • 各種授權型別,都可以很方便支援。
  • 資料儲存層,也可以隨意切換,比如切換為 Redis

相關文章