AOuth 原理可以看我之前的文章

选择开发包

基于PHP, 那么基本上只有一种选择了:
bshaffer/oauth2-server-php

# PHP 5.3.9+
composer require bshaffer/oauth2-server-php "^1.10"

第一次尝试

首先是官方文档: OAuth2 Server PHP.
根据官方的CookBook可以快速搭建一个DEMO.

注: 以下步骤均为官方文档中有的, 仅进行了翻译.

首先创建数据库

CREATE TABLE oauth_clients (
  client_id             VARCHAR(80)   NOT NULL,
  client_secret         VARCHAR(80),
  redirect_uri          VARCHAR(2000),
  grant_types           VARCHAR(80),
  scope                 VARCHAR(4000),
  user_id               VARCHAR(80),
  PRIMARY KEY (client_id)
);

CREATE TABLE oauth_access_tokens (
  access_token         VARCHAR(40)    NOT NULL,
  client_id            VARCHAR(80)    NOT NULL,
  user_id              VARCHAR(80),
  expires              TIMESTAMP      NOT NULL,
  scope                VARCHAR(4000),
  PRIMARY KEY (access_token)
);

CREATE TABLE oauth_authorization_codes (
  authorization_code  VARCHAR(40)     NOT NULL,
  client_id           VARCHAR(80)     NOT NULL,
  user_id             VARCHAR(80),
  redirect_uri        VARCHAR(2000),
  expires             TIMESTAMP       NOT NULL,
  scope               VARCHAR(4000),
  id_token            VARCHAR(1000),
  PRIMARY KEY (authorization_code)
);

CREATE TABLE oauth_refresh_tokens (
  refresh_token       VARCHAR(40)     NOT NULL,
  client_id           VARCHAR(80)     NOT NULL,
  user_id             VARCHAR(80),
  expires             TIMESTAMP       NOT NULL,
  scope               VARCHAR(4000),
  PRIMARY KEY (refresh_token)
);

CREATE TABLE oauth_users (
  username            VARCHAR(80),
  password            VARCHAR(80),
  first_name          VARCHAR(80),
  last_name           VARCHAR(80),
  email               VARCHAR(80),
  email_verified      BOOLEAN,
  scope               VARCHAR(4000),
  PRIMARY KEY (username)
);

CREATE TABLE oauth_scopes (
  scope               VARCHAR(80)     NOT NULL,
  is_default          BOOLEAN,
  PRIMARY KEY (scope)
);

CREATE TABLE oauth_jwt (
  client_id           VARCHAR(80)     NOT NULL,
  subject             VARCHAR(80),
  public_key          VARCHAR(2000)   NOT NULL
);

然后创建数据库连接和初始化服务
在目录下创建'server.php', 所有OAuth验证相关的都要引入该文件

$dsn      = 'mysql:dbname=my_oauth2_db;host=localhost';
$username = 'root';
$password = '';

// 引入开发包
require_once('oauth2-server-php/src/OAuth2/Autoloader.php');
OAuth2\Autoloader::register();

// 连接数据库
$storage = new OAuth2\Storage\Pdo(array('dsn' => $dsn, 'username' => $username, 'password' => $password));

// 初始化服务
$server = new OAuth2\Server($storage);

// 添加验证类型
$server->addGrantType(new OAuth2\GrantType\ClientCredentials($storage));
$server->addGrantType(new OAuth2\GrantType\AuthorizationCode($storage));

创建'token.php'

// 初始化
require_once __DIR__.'/server.php';

// 处理Token请求
$server->handleTokenRequest(OAuth2\Request::createFromGlobals())->send();

还没完, 你需要在数据库插入'oauth-clinet'数据

INSERT INTO oauth_clients (client_id, client_secret, redirect_uri) VALUES ("testclient", "testpass", "http://fake/");

这时候就可以请求了

curl -u testclient:testpass http://localhost/token.php -d 'grant_type=client_credentials' 
   {"access_token":"03807cb390319329bdf6c777d4dfae9c0d3b3c35","expires_in":3600,"token_type":"bearer","scope":null}

创建'resource.php'

// 初始化
require_once __DIR__.'/server.php';

// 校验资源请求
if (!$server->verifyResourceRequest(OAuth2\Request::createFromGlobals())) {
    $server->getResponse()->send();// 校验失败则输出开发包自带的错误输出
    die;
}

// 成功则输出自定义内容
echo json_encode(array('success' => true, 'message' => 'You accessed my APIs!'));

请求resource.php

curl http://localhost/resource.php -d 'access_token=YOUR_TOKEN'
{"success":true,"message":"You accessed my APIs!"}

创建'authorize.php'

// 初始化
require_once __DIR__.'/server.php';

$request = OAuth2\Request::createFromGlobals();// 获取GET/POST请求
$response = new OAuth2\Response();// 可以不传入, 会自动初始化

// 校验授权请求
if (!$server->validateAuthorizeRequest($request, $response)) {
    $response->send();// 校验失败则输出开发包自带的错误输出
    die;
}

// 如果POST为空则展示表单
if (empty($_POST)) {
  exit('
<form method="post">
  <label>Do You Authorize TestClient?</label><br />
  <input type="submit" name="authorized" value="yes">
  <input type="submit" name="authorized" value="no">
</form>');
}

// POST不为空则验证授权
$is_authorized = ($_POST['authorized'] === 'yes');
$server->handleAuthorizeRequest($request, $response, $is_authorized);

//此处与官方文档不同, 因为有IF判断和会阻止redirect_uri跳转, 在当前页面显示Authorization Code.
//if ($is_authorized) {
//  // this is only here so that you get to see your code in the cURL request. Otherwise, we'd redirect back to the client
//  $code = substr($response->getHttpHeader('Location'), strpos($response->getHttpHeader('Location'), 'code=')+5, 40);
//  /exit("SUCCESS! Authorization Code: $code");
}

// 输出(也就是跳转redirect_uri)
$response->send();

请求authorize.php

http://localhost/authorize.php?response_type=code&client_id=testclient&state=xyz&redirect_uri=http://localhost/callback.php

//拿到code后
curl -u testclient:testpass http://localhost/token.php -d 'grant_type=authorization_code&code=YOUR_CODE'
{"access_token":"6f05ad622a3d32a5a81aee5d73a5826adb8cbf63","expires_in":3600,"token_type":"bearer","scope":null}

至此DEMO全部完成.

模拟第三方登录

模拟第三方请求授权并登录
创建'callback.php'

注: 记得在'oauth_client'中修改redirect_uri

    //1.得到授权码(Authorization Code)
    $code = $_GET['code'];    
    //2.通过授权码得到令牌(Access_token)
    $post_data = array(
        'grant_type'=>'authorization_code',
        'client_id'=>'testclient',
        'client_secret'=>'testpass',
        'redirect_uri'=>'http://localhost/callback.php',
        'code'=>$code
    );
    $result = post('http://localhost/token.php',$post_data);
    $result = json_decode($result,true);
    $token = $result['access_token'];
    if(!$token) exit(var_dump($result));
    //3.通过令牌获取用户基本信息
    $userinfo = post('http://localhost/resource.php',['access_token'=>$token]);
    $userinfo = json_decode($userinfo,true);
    var_dump($userinfo);exit();
 
// 发送post请求
// @param string $url 请求地址
// @param array $post_data post键值对数据
// @return string
function post($url, $post_data){
    $o="";
    foreach ($post_data as $k=>$v)
    {
        $o.= "$k=".urlencode($v)."&";
    }
    $post_data=substr($o,0,-1);
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_URL,$url);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
    $result = curl_exec($ch);
    curl_close($ch);
    return $result;
}

此时就不用拿着Authorization Code去手动请求Access_token了, 会自行访问resource.php并输出结果

高级自定义

首先我讲个概念, 这个开发包分四个主要对象(?, 总之我是这样认为的).

$storage = new OAuth2\Storage\Pdo(array('dsn' => $dsn, 'username' => $username, 'password' => $password));
$storage // 数据库

$server = new OAuth2\Server($storage);
$server // 服务

$request = OAuth2\Request::createFromGlobals();
$request // 请求

$response = new OAuth2\Response();
$response // 响应

$storage 存储

所有数据都存储在这里.
Authorization Code相关的获取, 设置过期.
Access_token相关的获取, 设置过期.
oauth_user/oauth_client相关获取和设置.
scope相关获取和设置.
可以通过这个对象进行操作

$server 逻辑

逻辑校验在这里.
在这里你可以设置自定义的Controller对OAuth进行相应处理.
校验各种OAuth请求.

$request 请求

在这里可以获取客户端发送给服务器的数据.
POST/GET数据.
HTTP HEADER.

$response 响应

在这里可以获取和设置返回给客户端的响应.
设置和获取header, statecode.
设置和获取返回内容(一般为json).

例子

自定义错误页面

<h1>' . $response->getParameters()['error_description'] . '</h1>

获取oauth_client信息显示

授权给 ' . $storage->getClientDetails($request->query('client_id'))['client_name'] . '访问您的信息'

显示请求的scope

请求权限: ' . $storage->getClientScope($request->query('client_id'))

更多

原作者的代码注释非常详细, 主要函数都有说明. 没说明的函数从字面也可以看得出来.
多查看代码可以解锁更多地姿势. (#滑稽)

作者也定义了详细的原型(interface是这样叫的吗?).
可以非常方便的进行扩展.

最后修改:2021 年 06 月 06 日 07 : 00 PM