oauth2-server-php for windows 的那些坑

在windwos 环境下,使用vs2017 for php 工具进行调试时,总是搞不出来,

于是分析了一下原因,

首先,oauth2-server-php的环境是linux 而我在windows 环境下,步骤上是有些区别,但也不至于没办法调试

下面就分析一下每个步骤的细微区别“

1、windows 源码地址是,https://github.com/bshaffer/oauth2-server-php/releases 下载最新版本的,基本类同,

https://github.com/bshaffer/oauth2-server-php/releases

OAuth和OpenID的区别:
OAuth关注的是authorization授权,即:'用户能做什么';而OpenID侧重的是authentication认证,即:'用户是谁'。OpenID是用来认证协议,OAuth是授权协议,二者是互补的
OAuth 2.0将分为两个角色: Authorization server负责获取用户的授权并且发布token; Resource负责处理API calls。

如果用户的照片在A网站,他想要在B网站使用A网站的头像,并不需要向B网站提供自己在A网站的用户名和密码,而直接给B一个Access Token来获取A站的照片

具体流程如下:
1)用户访问网站B
2)B需要验证用户的身份
3)B将用户定向到A网站,用户输入帐号密码登录A网站
4)A网站询问是否要将Authentication的权利给B网站
5)用户告诉A站可以将认证权给B站
6)A网站把Authorization Code发给B站
7)B站用Autorization Code向A站换取Access Token
8)当B站拥有Access Token时,就拥有了用户在A站的一些访问权限
这是典型的Authorization Code Grant,常常运用于网络应用之中
还有Implicit Grant认证方式,这个则省去了AuthCode,开放平台直接返回access_token和有效期,用户ID等数据这种经常运用于手机客户端或者浏览器插件等没有在线服务器的应用
最后一种是Resource Owner Password Credentials Grant
这种是直接在应用中输入帐号密码,然后由应用XAuth技术将其提交给开放平台并得到Access Token
它经常用于PC可执行程序和手机应用,但由于存在一些争议,开发难度也较大,这里我就先不讨论他

使用 OAuth2-Server-php

 CREATE TABLE `oauth_access_tokens` (
`access_token` varchar(40) NOT NULL,
`client_id` varchar(80) NOT NULL,
`user_id` varchar(255) DEFAULT NULL,
`expires` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`scope` varchar(2000) DEFAULT NULL,
PRIMARY KEY (`access_token`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `oauth_authorization_codes` (
`authorization_code` varchar(40) NOT NULL,
`client_id` varchar(80) NOT NULL,
`user_id` varchar(255) DEFAULT NULL,
`redirect_uri` varchar(2000) DEFAULT NULL,
`expires` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`scope` varchar(2000) DEFAULT NULL,
PRIMARY KEY (`authorization_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `oauth_clients` (
`client_id` varchar(80) NOT NULL,
`client_secret` varchar(80) NOT NULL,
`redirect_uri` varchar(2000) NOT NULL,
`grant_types` varchar(80) DEFAULT NULL,
`scope` varchar(100) DEFAULT NULL,
`user_id` varchar(80) DEFAULT NULL,
PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `oauth_refresh_tokens` (
`refresh_token` varchar(40) NOT NULL,
`client_id` varchar(80) NOT NULL,
`user_id` varchar(255) DEFAULT NULL,
`expires` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`scope` varchar(2000) DEFAULT NULL,
PRIMARY KEY (`refresh_token`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `oauth_users` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`password` varchar(2000) DEFAULT NULL,
`first_name` varchar(255) DEFAULT NULL,
`last_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; CREATE TABLE `oauth_scopes` (
`scope` text,
`is_default` tinyint(1) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `oauth_jwt` (
`client_id` varchar(80) NOT NULL,
`subject` varchar(80) DEFAULT NULL,
`public_key` varchar(2000) DEFAULT NULL,
PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- test data
INSERT INTO oauth_clients (client_id, client_secret, redirect_uri) VALUES ("testclient", "testpass", "http://www.baidu.com/");
INSERT INTO oauth_users (username, password, first_name, last_name) VALUES ('rereadyou', '8551be07bab21f3933e8177538d411e43b78dbcc', 'bo', 'zhang');

各表的名字说明了表中存取的内容,表名可自定义,自定义位置为:OAuth2/Storage/Pdo.php 74行的 config 数组中,因为这里采用的是 mysql 数据库,所以需要修改的是 Pdo,若是采用其它的存储方案,如 Redis,则自行修改对应文件即可。注意这里的数据库名称是都是单数形式

/Pdo.php

         // debugging
$connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); $this->config = array_merge(array(
'client_table' => 'oauth_clients',
'access_token_table' => 'oauth_access_tokens',
'refresh_token_table' => 'oauth_refresh_tokens',
'code_table' => 'oauth_authorization_codes',
'user_table' => 'oauth_users',
'jwt_table' => 'oauth_jwt',
'jti_table' => 'oauth_jti',
'scope_table' => 'oauth_scopes',
'public_key_table' => 'oauth_public_keys',
), $config);

配置

我们来建立一个 server.php 文件来配置server,这个文件可以被所有的终端来调用。看require once就知道这个文件是平级的

 <?php
$dsn = 'mysql:dbname=test;host=localhost';
$username = 'root';
$password = 'orbit'; // error reporting (this is a demo, after all!)
ini_set('display_errors', 1);
error_reporting(E_ALL); // Autoloading (composer is preferred, but for this example let's just do this)
require_once('oauth2-server-php/src/OAuth2/Autoloader.php');
OAuth2\Autoloader::register(); // $dsn is the Data Source Name for your database, for exmaple "mysql:dbname=my_oauth2_db;host=localhost"
$storage = new OAuth2\Storage\Pdo(array('dsn' => $dsn, 'username' => $username, 'password' => $password)); // Pass a storage object or array of storage objects to the OAuth2 server class
//$server = new OAuth2\Server($storage);
$server = new OAuth2\Server($storage, array(
'allow_implicit' => true,
'refresh_token_lifetime'=> 2419200,
));
// Add the "Client Credentials" grant type (it is the simplest of the grant types)
$server->addGrantType(new OAuth2\GrantType\ClientCredentials($storage)); // Add the "Authorization Code" grant type (this is where the oauth magic happens)
$server->addGrantType(new OAuth2\GrantType\AuthorizationCode($storage));
//Resource Owner Password Credentials (资源所有者密码凭证许可)
$server->addGrantType(new OAuth2\GrantType\UserCredentials($storage));
//can RefreshToken set always_issue_new_refresh_token=true
$server->addGrantType(new OAuth2\GrantType\RefreshToken($storage, array(
'always_issue_new_refresh_token' => true
)));
// configure your available scopes
$defaultScope = 'basic';
$supportedScopes = array(
'basic',
'postonwall',
'accessphonenumber'
);
$memory = new OAuth2\Storage\Memory(array(
'default_scope' => $defaultScope,
'supported_scopes' => $supportedScopes
));
$scopeUtil = new OAuth2\Scope($memory);
$server->setScopeUtil($scopeUtil);

Token控制器

下面,我们将建立一个Token控制器,这个控制器URI将会返回OAuth2的Token给客户端

<?php
require_once __DIR__.'/server.php'; // Handle a request for an OAuth2.0 Access Token and send the response to the client
$server->handleTokenRequest(OAuth2\Request::createFromGlobals())->send();

测试Token控制器

Client Credentials Grant (客户端凭证许可)

curl -u testclient:testpass http://localhost/token.php -d 'grant_type=client_credentials' 

如果运行正常,则显示

{"access_token":"03807cb390319329bdf6c777d4dfae9c0d3b3c35","expires_in":3600,"token_type":"bearer","scope":null}  

资源控制器的建立和测试

你创建了Token,你需要在API中测试它,于是你写了如下代码

<?php
// include our OAuth2 Server object
require_once __DIR__ . '/server.php'; // Handle a request for an OAuth2.0 Access Token and send the response to the client
if (!$server->verifyResourceRequest(OAuth2\Request::createFromGlobals())) {
$server->getResponse()->send();
die;
}
echo json_encode(array('success' => true, 'message' => 'You accessed my APIs!'));

然后运行下面的命令,记得将YOUR_TOKEN替换成刚才得到的token,还有确保URL的正确

curl http://localhost/resource.php -d 'access_token=YOUR_TOKEN'  

如果没出问题,则会得到下面的结果

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

Authorization Code Grant (授权码认证)

Authorize.php代码

 <?php
// include our OAuth2 Server object
require_once __DIR__ . '/server.php'; $request = OAuth2\Request::createFromGlobals();
$response = new OAuth2\Response(); // validate the authorize request
if (!$server->validateAuthorizeRequest($request, $response)) {
$response->send();
die;
}
// display an authorization form
if (empty($_POST)) {
exit('
<form method="post">
<label>Do You Authorize TestClient?</label><br />
<input type="submit" name="authorized" value="yes">
</form>');
} // print the authorization code if the user has authorized your client
$is_authorized = ($_POST['authorized'] === 'yes');
$server->handleAuthorizeRequest($request, $response, $is_authorized);
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 AND DO redirect_uri! Authorization Code: $code");
}
$response->send();

然后在浏览器中打开这个URL

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

你将会看到一个表单,当你选择yes的时候会弹出你所获得的Authorization Code现在你可以用这个Authorization Code来刚才建立的token.php获得TOKEN,命令如下

curl -u testclient:testpass http://localhost/token.php -d 'grant_type=authorization_code&code=YOUR_CODE' 

就像刚才一样,你获得了一个TOKEN

{"access_token":"6ec6afa960587133d435d67d31e8ac08efda65ff","expires_in":3600,"token_type":"Bearer","scope":null,"refresh_token":"e57fafaa693a998b302ce9ec82d940d7325748d3"} 

请在30秒内完成这个操作,因为AuthorizationCode的有效期只有30秒,可以修改 OAuth2/ResponseType/AuthorizationCode.php 中的 AuthorizationCode class 的构造方法配置参数来自定义 authorization_code 有效时间.
access_token 有效期为3600s, refresh_token 有效期为 1209600s,可以在OAuth2/ResponseType/AccessToken.php 中的 AccessToken class 中的构造函数配置中进行修改。

可修改 OAuth2/GrantType/RefreshToken.php 中的 RefreshToken class __construct 方法中的 'always_issue_new_refresh_token' => true 来开启颁发新的 refresh_token.使用 refresh_token 换取 access_token:首先,刷新令牌必须使用授权码或资源所有者密码凭证许可类型检索:

curl -u testclient:testpass http:://localhost/token.php -d 'grant_type=refresh_token&refresh_token=YOUR_REFRESH_TOKEN'  

资源所有者密码凭证许可: user 表设计使用 sha1 摘要方式,没有添加 salt.

在 Pdo.php中有protected function checkPassword($user, $password)

curl -u testclient:testpass http://localhost/token.php -d 'grant_type=password&username=rereadyou&password=rereadyou'  

用Access Token联系本地用户

当你认证了一个用户并且分派了一个Token之后,你可能想知道彼时到底是哪个用户使用了这个Token
你可以使用handleAuthorizeRequest的可选参数user_id来完成,修改你的authorize.php文件

 $userid = 1; // A value on your server that identifies the user
$server->handleAuthorizeRequest($request, $response, $is_authorized, $userid);

这样一来,用户ID就伴随Token一起存进数据库了当Token被客户端使用的时候,你就知道是哪个用户了,修改resource.php来完成任务

 if (!$server->verifyResourceRequest(OAuth2\Request::createFromGlobals())) {
$server->getResponse()->send();
die;
}
$token = $server->getAccessTokenData(OAuth2\Request::createFromGlobals());
echo "User ID associated with this token is {$token['user_id']}";

scope 需要服务端确定具体的可行操作。

curl -u testclient:testpass http://localhost/token.php -d 'grant_type=client_credentials&scope=postonwall'  

scope 用来确定 client 所能进行的操作权限。项目中操作权限由 srbac 进行控制, Oauth2 中暂不做处理

 <?php
// include our OAuth2 Server object
require_once __DIR__ . '/server.php'; $request = OAuth2\Request::createFromGlobals();
$response = new OAuth2\Response();
$scopeRequired = 'postonwall'; // this resource requires "postonwall" scope
if (!$server->verifyResourceRequest($request, $response, $scopeRequired)) {
// if the scope required is different from what the token allows, this will send a "401 insufficient_scope" error
$server->getResponse()->send();
die;
}
echo json_encode(array('success' => true, 'message' => 'You accessed my APIs!'));

state 为 client app 在第一步骤中获取 authorization code 时向 OAuth2 Server 传递并由 OAuth2 Server 返回的随机哈希参数。state 参数主要用来防止跨站点请求伪造.

如果对整个调用请求中的参数进行排序,再以随机字符串nonce_str和timestamp加上排序后的参数来对整个调用生成1个sign,黑客即使截获sign,不同的时间点、参数请求所使用的sign也是不同的,难以伪造,自然会更安全。当然,写起来也更费事。加入随机字符串nonce_str主要保证签名不可预测。

 $sign = new SignGenerator($params);
$sign->onSortAfter(function($that) use($key) {
$that->key = $key;
});
$params['sign'] = $sign->getResult();

oauth2-server-php for windows 的那些坑 (研究中...)的更多相关文章

  1. 困扰多日的C#调用Haskell问题竟然是Windows的一个坑

    最近一直被C#调用Haskell时的“尝试读取或写入受保护的内存”问题所困扰(详见C#调用haskell遭遇Attempted to read or write protected memory,C# ...

  2. 第一次使用docker for windows 遇到的坑

    原文:第一次使用docker for windows 遇到的坑 1. 目前win10安装docker, 不需要安装其他工具,可直接到官网下载 2. 此版本的docker可同时运行Windows con ...

  3. windows container 踩坑记

    windows container 踩坑记 Intro 我们有一些服务是 dotnet framework 的,不能直接跑在 docker linux container 下面,最近一直在折腾把它部署 ...

  4. 使用 OAuth2-Server-php 在 Yii 框架上搭建 OAuth2 Server

    原文转自 http://www.cnblogs.com/ldms/p/4565547.html Yii 有很多 extension 可以使用,在查看了 Yii 官网上提供的与 OAuth 相关的扩展后 ...

  5. 配置SQL Server去使用 Windows的 Large-Page/Huge-Page allocations

    配置SQL Server去使用 Windows的 Large-Page/Huge-Page  allocations 目录表->页表->物理内存页 看这篇文章之前可以先看一下下面这篇文章 ...

  6. minikube windows hyperx填坑记

    minikube windows hyperx填坑记 安装了一天半,还是没行,先放弃 开始 minikube start --vm-driver=hyperv --hyperv-virtual-swi ...

  7. 使用 OAuth2-Server-php 搭建 OAuth2 Server

    Yii 有很多 extension 可以使用,在查看了 Yii 官网上提供的与 OAuth 相关的扩展后,发现了几个 OAuth2 的客户端扩展,但是并没有找到可以作为 OAuth2 Server 的 ...

  8. Installing OpenSSH from the Settings UI on Windows Server 2019 or Windows 10 1809

    Installing OpenSSH from the Settings UI on Windows Server 2019 or Windows 10 1809 OpenSSH client and ...

  9. CAS3.5.x(x>1)支持OAuth2 server

    国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送)国内私 ...

随机推荐

  1. git零基础【慢慢补充】

    git branch dev   //创建新分支 git checkout dev   //切换到新分支 git add .  //把当前修改加到暂存区 git commit -m "代码描 ...

  2. 射洪中学讲课PPT

    咸鱼跑去射洪中学讲课去啦. 高中生好萌呀!!!! 下面是讲课PPT day3 简单吹了一波noip2015如何骗分,所以课件就是去网上到处kuai题解而已,所以课件我就不上传了. day4 题目+题解 ...

  3. Caffe2(1)----Ubantu14.04安装

    英文好的请直接参考官方安装文档:Ubantu14.04下的源码编译. Caffe2的安装相比以前Caffe一代的安装,简直有点一键装机的感觉,下面简单总结下Caffe2的安装. 环境:Ubantu14 ...

  4. Git_配置别名

    有没有经常敲错命令?比如git status?status这个单词真心不好记. 如果敲git st就表示git status那就简单多了,当然这种偷懒的办法我们是极力赞成的. 我们只需要敲一行命令,告 ...

  5. HTML5学习笔记4

    10.表单元素表单元素用于获取用户的输入数据form 表示HTML表单属性:    action 表示表单提交的页面    method 表示表单提交的请求方式:有POST和GET两种,默认GET(P ...

  6. 精简高效CSS系列之二——浮动float

    一.浮动基础知识 假如一个页面上有3个div块,如下排列: 图1:不使用浮动 图2:向右浮动 图2说明了框1脱离了文档流向右移动,直到它的右边缘碰到包含框的右边缘为止. 图3:向左浮动 图3说明了框1 ...

  7. ConcurrentDictionary AddOrUpdate

    var sessionId = a.Session.SessionID.ToString(); userDic.AddOrUpdate( authUser.UserId, sessionId, (ke ...

  8. pytest文档26-运行上次失败用例(--lf 和 --ff)

    前言 "80%的bug集中在20%的模块,越是容易出现bug的模块,bug是越改越多"平常我们做手工测试的时候,比如用100个用例需要执行,其中10个用例失败了, 当开发修复完bu ...

  9. UITableView分页

    UITableView分页上拉加载简单,ARC环境,源码如下,以作备份: 原理是,点击最后一个cell,触发一个事件来处理数据,然后reloadData RootViewController.m + ...

  10. Jquery Mobile实例--利用优酷JSON接口读取视频数据

    本文将介绍,如何利用JqueryMobile调用优酷API JSON接口显示视频数据. (1)注册用户接口. 首页,到 http://open.youku.com 注册一个账户,并通过验证.然后找到A ...