oauth2.0服务端与客户端搭建 - 推酷

今天搭建了oauth2.0服务端与客户端。把搭建的过程记录一下。具体实现的功能是:client.ruanwenwu.cn的用户能够通过 server.ruanwenwu.cn的用户名和密码登陆client.ruanwenwu.cn。并且登陆 后,client.ruanwenwu.cn的用户能获取server.ruanwenwu.cn哪里的一些资源。

我的个人博客原文地址: http://www.ruanwenwu.cn/2015/12/oauth-server-and-client-install.html

一、oauth2.0的作用

1、搭建第三方登录平台(就像你用很多网站支持qq登录。其实是qq提供的oauth2.0服务端支持。网站作为第三方,只要用oauth2.0标准请求服务端,求能得到用户信息,并实现登录)。

2、公共资源管理。

就 像你(Y)想通过一个打印照片的网站(A)来打印你存放在google(B)的照片。你就要通过这个A来获取B的照片。B肯定要验证是否是他的用户Y的请 求,验证通过才把照片传递给A实现打印。问题是Y不想把账号密码直接给A。用oauth2.0就是来解决这个问题的:

A先 把Y导向B的站点进行授权。授权通过,B向A发送oauth_code。A拿到这个oauth_code,连同A的client_id和 oauth_secrete发送给B,如果数据正确,B就会给A发送oauth_token。有了这个token,A就可以获取B的相关资源的。

基本的流程是这样。下面是具体的实现过程。

二、oauth2.0服务端的搭建。

oauth2.0的服务端分为验证服务器和资源服务器。这两个服务器也可以是同一个服务器(只不过把这两个功能用一个 服务器来完成罢了)。

说明:我的实现都在Thinkphp3.2.3框架下实现。

1、下载thinkphp3.2.3。

2、配置虚拟机server.ruanwenwu.cn

3、在Thinkphp的Home应用下修改配置文件(server.ruanwenwu.cn/Application/Home/Conf/config.php)如下所示:

  1. <?php
  2. return array(
  3. //'配置项'=>'配置值'
  4. //数据库设置
  5. 'DB_TYPE' => 'mysql',
  6. 'DB_HOST' => '127.0.0.1',//localhost
  7. 'DB_NAME' => 'oauth',
  8. 'DB_USER' => 'root',
  9. 'DB_PWD' => 'root',
  10. 'DB_PORT' => '3306',
  11. 'SCOPE_INFO' => array( //这是授权选项。根据你自己的项目来
  12. array(
  13. 'name' => '个人信息',
  14. 'value' => 'basicinfo'
  15. ),
  16. array(
  17. 'name' => '论坛发帖回帖',
  18. 'value' => 'bbsinfo'
  19. ),
  20. ),
  21. 'OAUTH2_CODES_TABLE' =>'oauth_code', //这里是oauth项目需要用的三个基础表
  22. 'OAUTH2_CLIENTS_TABLE' =>'oauth_client',
  23. 'OAUTH2_TOKEN_TABLE' =>'oauth_token',
  24. 'SECRETKYE' => 'Mumayi!@#', //下面是一些网站自定义的项目。可以根据自己的情况来写或者不写
  25. //session 有效期
  26. 'SESSION_EXPIRES' => 1200,
  27. //key 有效期
  28. 'PASS_KEY_EXPIRES' => 86400,
  29. //key 有效期
  30. 'PHONE_KEY_EXPIRES' => 300,
  31. //key 加密 整型 数字 必须为 int
  32. 'PASS_KEY_CALC' => 1314,
  33. );

4、建oauth表。

  1. SET FOREIGN_KEY_CHECKS=0;
  2. -- ----------------------------
  3. -- Table structure for oauth_client
  4. -- ----------------------------
  5. DROP TABLE IF EXISTS `oauth_client`;
  6. CREATE TABLE `oauth_client` (
  7. `id` bigint(20) NOT NULL AUTO_INCREMENT,
  8. `client_id` varchar(32) NOT NULL,
  9. `client_secret` varchar(32) NOT NULL,
  10. `redirect_uri` varchar(200) NOT NULL,
  11. `create_time` int(11) NOT NULL,
  12. PRIMARY KEY (`id`)
  13. ) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
  14. -- ----------------------------
  15. -- Table structure for oauth_code
  16. -- ----------------------------
  17. DROP TABLE IF EXISTS `oauth_code`;
  18. CREATE TABLE `oauth_code` (
  19. `id` bigint(20) NOT NULL AUTO_INCREMENT,
  20. `client_id` varchar(32) NOT NULL,
  21. `user_id` int(11) NOT NULL DEFAULT '1',
  22. `code` varchar(40) NOT NULL,
  23. `redirect_uri` varchar(200) NOT NULL,
  24. `expires` int(11) NOT NULL,
  25. `scope` varchar(250) DEFAULT NULL,
  26. PRIMARY KEY (`id`)
  27. ) ENGINE=MyISAM AUTO_INCREMENT=57 DEFAULT CHARSET=utf8;
  28. -- ----------------------------
  29. -- Table structure for oauth_token
  30. -- ----------------------------
  31. DROP TABLE IF EXISTS `oauth_token`;
  32. CREATE TABLE `oauth_token` (
  33. `id` bigint(20) NOT NULL AUTO_INCREMENT,
  34. `client_id` varchar(32) NOT NULL,
  35. `user_id` int(11) NOT NULL,
  36. `access_token` varchar(40) NOT NULL,
  37. `refresh_token` varchar(40) NOT NULL,
  38. `expires_in` int(11) NOT NULL,
  39. `scope` varchar(200) DEFAULT NULL,
  40. PRIMARY KEY (`id`)
  41. ) ENGINE=MyISAM AUTO_INCREMENT=26 DEFAULT CHARSET=utf8;

5、引入oauth2.0服务端类文件。

5-1、在\server.ruanwenwu.cn\ThinkPHP\Library\Vendor\目录下建立oauth目录。

5-2、引入oauth2.0服务端PHP脚本。

在刚建立的oauth目录下引入OAuth2.class.php这个文件基本不用做修改。我们的操作都在继承这个类的子类上完成。类的代码如下:

  1. <?php
  2. /**
  3. * @mainpage
  4. * OAuth 2.0 server in PHP, originally written for
  5. * <a href="http://www.opendining.net/"> Open Dining</a>. Supports
  6. * <a href="http://tools.ietf.org/html/draft-ietf-oauth-v2-10">IETF draft v10</a>.
  7. *
  8. * Source repo has sample servers implementations for
  9. * <a href="http://php.net/manual/en/book.pdo.php"> PHP Data Objects</a> and
  10. * <a href="http://www.mongodb.org/">MongoDB</a>. Easily adaptable to other
  11. * storage engines.
  12. *
  13. * PHP Data Objects supports a variety of databases, including MySQL,
  14. * Microsoft SQL Server, SQLite, and Oracle, so you can try out the sample
  15. * to see how it all works.
  16. *
  17. * We're expanding the wiki to include more helpful documentation, but for
  18. * now, your best bet is to view the oauth.php source - it has lots of
  19. * comments.
  20. *
  21. * @author Tim Ridgely <tim.ridgely@gmail.com>
  22. * @author Aaron Parecki <aaron@parecki.com>
  23. * @author Edison Wong <hswong3i@pantarei-design.com>
  24. *
  25. * @see http://code.google.com/p/oauth2-php/
  26. */
  27. /**
  28. * The default duration in seconds of the access token lifetime.
  29. */
  30. define("OAUTH2_DEFAULT_ACCESS_TOKEN_LIFETIME", 3600);
  31. /**
  32. * The default duration in seconds of the authorization code lifetime.
  33. */
  34. define("OAUTH2_DEFAULT_AUTH_CODE_LIFETIME", 30);
  35. /**
  36. * The default duration in seconds of the refresh token lifetime.
  37. */
  38. define("OAUTH2_DEFAULT_REFRESH_TOKEN_LIFETIME", 1209600);
  39. /**
  40. * @defgroup oauth2_section_2 Client Credentials
  41. * @{
  42. *
  43. * When interacting with the authorization server, the client identifies
  44. * itself using a client identifier and authenticates using a set of
  45. * client credentials. This specification provides one mechanism for
  46. * authenticating the client using password credentials.
  47. *
  48. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-2
  49. */
  50. /**
  51. * Regex to filter out the client identifier (described in Section 2 of IETF draft).
  52. *
  53. * IETF draft does not prescribe a format for these, however I've arbitrarily
  54. * chosen alphanumeric strings with hyphens and underscores, 3-32 characters
  55. * long.
  56. *
  57. * Feel free to change.
  58. */
  59. define("OAUTH2_CLIENT_ID_REGEXP", "/^[a-z0-9-_]{3,32}$/i");
  60. /**
  61. * @}
  62. */
  63. /**
  64. * @defgroup oauth2_section_3 Obtaining End-User Authorization
  65. * @{
  66. *
  67. * When the client interacts with an end-user, the end-user MUST first
  68. * grant the client authorization to access its protected resources.
  69. * Once obtained, the end-user access grant is expressed as an
  70. * authorization code which the client uses to obtain an access token.
  71. * To obtain an end-user authorization, the client sends the end-user to
  72. * the end-user authorization endpoint.
  73. *
  74. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-3
  75. */
  76. /**
  77. * Denotes "token" authorization response type.
  78. */
  79. define("OAUTH2_AUTH_RESPONSE_TYPE_ACCESS_TOKEN", "token");
  80. /**
  81. * Denotes "code" authorization response type.
  82. */
  83. define("OAUTH2_AUTH_RESPONSE_TYPE_AUTH_CODE", "code");
  84. /**
  85. * Denotes "code-and-token" authorization response type.
  86. */
  87. define("OAUTH2_AUTH_RESPONSE_TYPE_CODE_AND_TOKEN", "code-and-token");
  88. /**
  89. * Regex to filter out the authorization response type.
  90. */
  91. define("OAUTH2_AUTH_RESPONSE_TYPE_REGEXP", "/^(token|code|code-and-token)$/");
  92. /**
  93. * @}
  94. */
  95. /**
  96. * @defgroup oauth2_section_4 Obtaining an Access Token
  97. * @{
  98. *
  99. * The client obtains an access token by authenticating with the
  100. * authorization server and presenting its access grant (in the form of
  101. * an authorization code, resource owner credentials, an assertion, or a
  102. * refresh token).
  103. *
  104. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4
  105. */
  106. /**
  107. * Denotes "authorization_code" grant types (for token obtaining).
  108. */
  109. define("OAUTH2_GRANT_TYPE_AUTH_CODE", "authorization_code");
  110. /**
  111. * Denotes "password" grant types (for token obtaining).
  112. */
  113. define("OAUTH2_GRANT_TYPE_USER_CREDENTIALS", "password");
  114. /**
  115. * Denotes "assertion" grant types (for token obtaining).
  116. */
  117. define("OAUTH2_GRANT_TYPE_ASSERTION", "assertion");
  118. /**
  119. * Denotes "refresh_token" grant types (for token obtaining).
  120. */
  121. define("OAUTH2_GRANT_TYPE_REFRESH_TOKEN", "refresh_token");
  122. /**
  123. * Denotes "none" grant types (for token obtaining).
  124. */
  125. define("OAUTH2_GRANT_TYPE_NONE", "none");
  126. /**
  127. * Regex to filter out the grant type.
  128. */
  129. define("OAUTH2_GRANT_TYPE_REGEXP", "/^(authorization_code|password|assertion|refresh_token|none)$/");
  130. /**
  131. * @}
  132. */
  133. /**
  134. * @defgroup oauth2_section_5 Accessing a Protected Resource
  135. * @{
  136. *
  137. * Clients access protected resources by presenting an access token to
  138. * the resource server. Access tokens act as bearer tokens, where the
  139. * token string acts as a shared symmetric secret. This requires
  140. * treating the access token with the same care as other secrets (e.g.
  141. * end-user passwords). Access tokens SHOULD NOT be sent in the clear
  142. * over an insecure channel.
  143. *
  144. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5
  145. */
  146. /**
  147. * Used to define the name of the OAuth access token parameter (POST/GET/etc.).
  148. *
  149. * IETF Draft sections 5.1.2 and 5.1.3 specify that it should be called
  150. * "oauth_token" but other implementations use things like "access_token".
  151. *
  152. * I won't be heartbroken if you change it, but it might be better to adhere
  153. * to the spec.
  154. *
  155. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5.1.2
  156. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5.1.3
  157. */
  158. define("OAUTH2_TOKEN_PARAM_NAME", "oauth_token");
  159. /**
  160. * @}
  161. */
  162. /**
  163. * @defgroup oauth2_http_status HTTP status code
  164. * @{
  165. */
  166. /**
  167. * "Found" HTTP status code.
  168. *
  169. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-3
  170. */
  171. define("OAUTH2_HTTP_FOUND", "302 Found");
  172. /**
  173. * "Bad Request" HTTP status code.
  174. *
  175. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.3
  176. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5.2.1
  177. */
  178. define("OAUTH2_HTTP_BAD_REQUEST", "400 Bad Request");
  179. /**
  180. * "Unauthorized" HTTP status code.
  181. *
  182. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.3
  183. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5.2.1
  184. */
  185. define("OAUTH2_HTTP_UNAUTHORIZED", "401 Unauthorized");
  186. /**
  187. * "Forbidden" HTTP status code.
  188. *
  189. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5.2.1
  190. */
  191. define("OAUTH2_HTTP_FORBIDDEN", "403 Forbidden");
  192. /**
  193. * @}
  194. */
  195. /**
  196. * @defgroup oauth2_error Error handling
  197. * @{
  198. *
  199. * @todo Extend for i18n.
  200. */
  201. /**
  202. * The request is missing a required parameter, includes an unsupported
  203. * parameter or parameter value, or is otherwise malformed.
  204. *
  205. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-3.2.1
  206. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.3.1
  207. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5.2.1
  208. */
  209. define("OAUTH2_ERROR_INVALID_REQUEST", "invalid_request");
  210. /**
  211. * The client identifier provided is invalid.
  212. *
  213. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-3.2.1
  214. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.3.1
  215. */
  216. define("OAUTH2_ERROR_INVALID_CLIENT", "invalid_client");
  217. /**
  218. * The client is not authorized to use the requested response type.
  219. *
  220. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-3.2.1
  221. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.3.1
  222. */
  223. define("OAUTH2_ERROR_UNAUTHORIZED_CLIENT", "unauthorized_client");
  224. /**
  225. * The redirection URI provided does not match a pre-registered value.
  226. *
  227. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-3.2.1
  228. */
  229. define("OAUTH2_ERROR_REDIRECT_URI_MISMATCH", "redirect_uri_mismatch");
  230. /**
  231. * The end-user or authorization server denied the request.
  232. *
  233. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-3.2.1
  234. */
  235. define("OAUTH2_ERROR_USER_DENIED", "access_denied");
  236. /**
  237. * The requested response type is not supported by the authorization server.
  238. *
  239. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-3.2.1
  240. */
  241. define("OAUTH2_ERROR_UNSUPPORTED_RESPONSE_TYPE", "unsupported_response_type");
  242. /**
  243. * The requested scope is invalid, unknown, or malformed.
  244. *
  245. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-3.2.1
  246. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.3.1
  247. */
  248. define("OAUTH2_ERROR_INVALID_SCOPE", "invalid_scope");
  249. /**
  250. * The provided access grant is invalid, expired, or revoked (e.g. invalid
  251. * assertion, expired authorization token, bad end-user password credentials,
  252. * or mismatching authorization code and redirection URI).
  253. *
  254. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.3.1
  255. */
  256. define("OAUTH2_ERROR_INVALID_GRANT", "invalid_grant");
  257. /**
  258. * The access grant included - its type or another attribute - is not
  259. * supported by the authorization server.
  260. *
  261. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.3.1
  262. */
  263. define("OAUTH2_ERROR_UNSUPPORTED_GRANT_TYPE", "unsupported_grant_type");
  264. /**
  265. * The access token provided is invalid. Resource servers SHOULD use this
  266. * error code when receiving an expired token which cannot be refreshed to
  267. * indicate to the client that a new authorization is necessary. The resource
  268. * server MUST respond with the HTTP 401 (Unauthorized) status code.
  269. *
  270. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5.2.1
  271. */
  272. define("OAUTH2_ERROR_INVALID_TOKEN", "invalid_token");
  273. /**
  274. * The access token provided has expired. Resource servers SHOULD only use
  275. * this error code when the client is expected to be able to handle the
  276. * response and request a new access token using the refresh token issued
  277. * with the expired access token. The resource server MUST respond with the
  278. * HTTP 401 (Unauthorized) status code.
  279. *
  280. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5.2.1
  281. */
  282. define("OAUTH2_ERROR_EXPIRED_TOKEN", "expired_token");
  283. /**
  284. * The request requires higher privileges than provided by the access token.
  285. * The resource server SHOULD respond with the HTTP 403 (Forbidden) status
  286. * code and MAY include the "scope" attribute with the scope necessary to
  287. * access the protected resource.
  288. *
  289. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5.2.1
  290. */
  291. define("OAUTH2_ERROR_INSUFFICIENT_SCOPE", "insufficient_scope");
  292. /**
  293. * @}
  294. */
  295. /**
  296. * OAuth2.0 draft v10 server-side implementation.
  297. *
  298. * @author Originally written by Tim Ridgely <tim.ridgely@gmail.com>.
  299. * @author Updated to draft v10 by Aaron Parecki <aaron@parecki.com>.
  300. * @author Debug, coding style clean up and documented by Edison Wong <hswong3i@pantarei-design.com>.
  301. */
  302. abstract class OAuth2 {
  303. /**
  304. * Array of persistent variables stored.
  305. */
  306. protected $conf = array();
  307. /**
  308. * Returns a persistent variable.
  309. *
  310. * To avoid problems, always use lower case for persistent variable names.
  311. *
  312. * @param $name
  313. * The name of the variable to return.
  314. * @param $default
  315. * The default value to use if this variable has never been set.
  316. *
  317. * @return
  318. * The value of the variable.
  319. */
  320. public function getVariable($name, $default = NULL) {
  321. return isset($this->conf[$name]) ? $this->conf[$name] : $default;
  322. }
  323. /**
  324. * Sets a persistent variable.
  325. *
  326. * To avoid problems, always use lower case for persistent variable names.
  327. *
  328. * @param $name
  329. * The name of the variable to set.
  330. * @param $value
  331. * The value to set.
  332. */
  333. public function setVariable($name, $value) {
  334. $this->conf[$name] = $value;
  335. return $this;
  336. }
  337. // Subclasses must implement the following functions.
  338. /**
  339. * Make sure that the client credentials is valid.
  340. *
  341. * @param $client_id
  342. * Client identifier to be check with.
  343. * @param $client_secret
  344. * (optional) If a secret is required, check that they've given the right one.
  345. *
  346. * @return
  347. * TRUE if client credentials are valid, and MUST return FALSE if invalid.
  348. *
  349. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-2.1
  350. *
  351. * @ingroup oauth2_section_2
  352. */
  353. abstract protected function checkClientCredentials($client_id, $client_secret = NULL);
  354. /**
  355. * Get the registered redirect URI of corresponding client_id.
  356. *
  357. * OAuth says we should store request URIs for each registered client.
  358. * Implement this function to grab the stored URI for a given client id.
  359. *
  360. * @param $client_id
  361. * Client identifier to be check with.
  362. *
  363. * @return
  364. * Registered redirect URI of corresponding client identifier, and MUST
  365. * return FALSE if the given client does not exist or is invalid.
  366. *
  367. * @ingroup oauth2_section_3
  368. */
  369. abstract protected function getRedirectUri($client_id);
  370. /**
  371. * Look up the supplied oauth_token from storage.
  372. *
  373. * We need to retrieve access token data as we create and verify tokens.
  374. *
  375. * @param $oauth_token
  376. * oauth_token to be check with.
  377. *
  378. * @return
  379. * An associative array as below, and return NULL if the supplied oauth_token
  380. * is invalid:
  381. * - client_id: Stored client identifier.
  382. * - expires: Stored expiration in unix timestamp.
  383. * - scope: (optional) Stored scope values in space-separated string.
  384. *
  385. * @ingroup oauth2_section_5
  386. */
  387. abstract protected function getAccessToken($oauth_token);
  388. /**
  389. * Store the supplied access token values to storage.
  390. *
  391. * We need to store access token data as we create and verify tokens.
  392. *
  393. * @param $oauth_token
  394. * oauth_token to be stored.
  395. * @param $client_id
  396. * Client identifier to be stored.
  397. * @param $expires
  398. * Expiration to be stored.
  399. * @param $user_id
  400. * User ID
  401. * @param $scope
  402. * (optional) Scopes to be stored in space-separated string.
  403. *
  404. * @ingroup oauth2_section_4
  405. */
  406. abstract protected function setAccessToken($oauth_token, $client_id, $expires, $user_id, $scope=NULL, $refresh_token='');
  407. // Stuff that should get overridden by subclasses.
  408. //
  409. // I don't want to make these abstract, because then subclasses would have
  410. // to implement all of them, which is too much work.
  411. //
  412. // So they're just stubs. Override the ones you need.
  413. /**
  414. * Return supported grant types.
  415. *
  416. * You should override this function with something, or else your OAuth
  417. * provider won't support any grant types!
  418. *
  419. * @return
  420. * A list as below. If you support all grant types, then you'd do:
  421. * @code
  422. * return array(
  423. * OAUTH2_GRANT_TYPE_AUTH_CODE,
  424. * OAUTH2_GRANT_TYPE_USER_CREDENTIALS,
  425. * OAUTH2_GRANT_TYPE_ASSERTION,
  426. * OAUTH2_GRANT_TYPE_REFRESH_TOKEN,
  427. * OAUTH2_GRANT_TYPE_NONE,
  428. * );
  429. * @endcode
  430. *
  431. * @ingroup oauth2_section_4
  432. */
  433. protected function getSupportedGrantTypes() {
  434. return array();
  435. }
  436. /**
  437. * Return supported authorization response types.
  438. *
  439. * You should override this function with your supported response types.
  440. *
  441. * @return
  442. * A list as below. If you support all authorization response types,
  443. * then you'd do:
  444. * @code
  445. * return array(
  446. * OAUTH2_AUTH_RESPONSE_TYPE_AUTH_CODE,
  447. * OAUTH2_AUTH_RESPONSE_TYPE_ACCESS_TOKEN,
  448. * OAUTH2_AUTH_RESPONSE_TYPE_CODE_AND_TOKEN,
  449. * );
  450. * @endcode
  451. *
  452. * @ingroup oauth2_section_3
  453. */
  454. protected function getSupportedAuthResponseTypes() {
  455. return array(
  456. OAUTH2_AUTH_RESPONSE_TYPE_AUTH_CODE,
  457. OAUTH2_AUTH_RESPONSE_TYPE_ACCESS_TOKEN,
  458. OAUTH2_AUTH_RESPONSE_TYPE_CODE_AND_TOKEN
  459. );
  460. }
  461. /**
  462. * Return supported scopes.
  463. *
  464. * If you want to support scope use, then have this function return a list
  465. * of all acceptable scopes (used to throw the invalid-scope error).
  466. *
  467. * @return
  468. * A list as below, for example:
  469. * @code
  470. * return array(
  471. * 'my-friends',
  472. * 'photos',
  473. * 'whatever-else',
  474. * );
  475. * @endcode
  476. *
  477. * @ingroup oauth2_section_3
  478. */
  479. protected function getSupportedScopes() {
  480. return array();
  481. }
  482. /**
  483. * Check restricted authorization response types of corresponding Client
  484. * identifier.
  485. *
  486. * If you want to restrict clients to certain authorization response types,
  487. * override this function.
  488. *
  489. * @param $client_id
  490. * Client identifier to be check with.
  491. * @param $response_type
  492. * Authorization response type to be check with, would be one of the
  493. * values contained in OAUTH2_AUTH_RESPONSE_TYPE_REGEXP.
  494. *
  495. * @return
  496. * TRUE if the authorization response type is supported by this
  497. * client identifier, and FALSE if it isn't.
  498. *
  499. * @ingroup oauth2_section_3
  500. */
  501. protected function checkRestrictedAuthResponseType($client_id, $response_type) {
  502. return TRUE;
  503. }
  504. /**
  505. * Check restricted grant types of corresponding client identifier.
  506. *
  507. * If you want to restrict clients to certain grant types, override this
  508. * function.
  509. *
  510. * @param $client_id
  511. * Client identifier to be check with.
  512. * @param $grant_type
  513. * Grant type to be check with, would be one of the values contained in
  514. * OAUTH2_GRANT_TYPE_REGEXP.
  515. *
  516. * @return
  517. * TRUE if the grant type is supported by this client identifier, and
  518. * FALSE if it isn't.
  519. *
  520. * @ingroup oauth2_section_4
  521. */
  522. protected function checkRestrictedGrantType($client_id, $grant_type) {
  523. return TRUE;
  524. }
  525. // Functions that help grant access tokens for various grant types.
  526. /**
  527. * Fetch authorization code data (probably the most common grant type).
  528. *
  529. * Retrieve the stored data for the given authorization code.
  530. *
  531. * Required for OAUTH2_GRANT_TYPE_AUTH_CODE.
  532. *
  533. * @param $code
  534. * Authorization code to be check with.
  535. *
  536. * @return
  537. * An associative array as below, and NULL if the code is invalid:
  538. * - client_id: Stored client identifier.
  539. * - redirect_uri: Stored redirect URI.
  540. * - expires: Stored expiration in unix timestamp.
  541. * - scope: (optional) Stored scope values in space-separated string.
  542. *
  543. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.1.1
  544. *
  545. * @ingroup oauth2_section_4
  546. */
  547. protected function getAuthCode($code) {
  548. return NULL;
  549. }
  550. /**
  551. * Take the provided authorization code values and store them somewhere.
  552. *
  553. * This function should be the storage counterpart to getAuthCode().
  554. *
  555. * If storage fails for some reason, we're not currently checking for
  556. * any sort of success/failure, so you should bail out of the script
  557. * and provide a descriptive fail message.
  558. *
  559. * Required for OAUTH2_GRANT_TYPE_AUTH_CODE.
  560. *
  561. * @param $code
  562. * Authorization code to be stored.
  563. * @param $client_id
  564. * Client identifier to be stored.
  565. * @param $redirect_uri
  566. * Redirect URI to be stored.
  567. * @param $expires
  568. * Expiration to be stored.
  569. * @param $scope
  570. * (optional) Scopes to be stored in space-separated string.
  571. *
  572. * @ingroup oauth2_section_4
  573. */
  574. protected function setAuthCode($code, $client_id, $redirect_uri, $expires, $userId, $scope = NULL) {
  575. }
  576. /**
  577. * Grant access tokens for basic user credentials.
  578. *
  579. * Check the supplied username and password for validity.
  580. *
  581. * You can also use the $client_id param to do any checks required based
  582. * on a client, if you need that.
  583. *
  584. * Required for OAUTH2_GRANT_TYPE_USER_CREDENTIALS.
  585. *
  586. * @param $client_id
  587. * Client identifier to be check with.
  588. * @param $username
  589. * Username to be check with.
  590. * @param $password
  591. * Password to be check with.
  592. *
  593. * @return
  594. * TRUE if the username and password are valid, and FALSE if it isn't.
  595. * Moreover, if the username and password are valid, and you want to
  596. * verify the scope of a user's access, return an associative array
  597. * with the scope values as below. We'll check the scope you provide
  598. * against the requested scope before providing an access token:
  599. * @code
  600. * return array(
  601. * 'scope' => <stored scope values (space-separated string)>,
  602. * );
  603. * @endcode
  604. *
  605. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.1.2
  606. *
  607. * @ingroup oauth2_section_4
  608. */
  609. protected function checkUserCredentials($client_id, $username, $password) {
  610. return FALSE;
  611. }
  612. /**
  613. * Grant access tokens for assertions.
  614. *
  615. * Check the supplied assertion for validity.
  616. *
  617. * You can also use the $client_id param to do any checks required based
  618. * on a client, if you need that.
  619. *
  620. * Required for OAUTH2_GRANT_TYPE_ASSERTION.
  621. *
  622. * @param $client_id
  623. * Client identifier to be check with.
  624. * @param $assertion_type
  625. * The format of the assertion as defined by the authorization server.
  626. * @param $assertion
  627. * The assertion.
  628. *
  629. * @return
  630. * TRUE if the assertion is valid, and FALSE if it isn't. Moreover, if
  631. * the assertion is valid, and you want to verify the scope of an access
  632. * request, return an associative array with the scope values as below.
  633. * We'll check the scope you provide against the requested scope before
  634. * providing an access token:
  635. * @code
  636. * return array(
  637. * 'scope' => <stored scope values (space-separated string)>,
  638. * );
  639. * @endcode
  640. *
  641. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.1.3
  642. *
  643. * @ingroup oauth2_section_4
  644. */
  645. protected function checkAssertion($client_id, $assertion_type, $assertion) {
  646. return FALSE;
  647. }
  648. /**
  649. * Grant refresh access tokens.
  650. *
  651. * Retrieve the stored data for the given refresh token.
  652. *
  653. * Required for OAUTH2_GRANT_TYPE_REFRESH_TOKEN.
  654. *
  655. * @param $refresh_token
  656. * Refresh token to be check with.
  657. *
  658. * @return
  659. * An associative array as below, and NULL if the refresh_token is
  660. * invalid:
  661. * - client_id: Stored client identifier.
  662. * - expires: Stored expiration unix timestamp.
  663. * - scope: (optional) Stored scope values in space-separated string.
  664. *
  665. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.1.4
  666. *
  667. * @ingroup oauth2_section_4
  668. */
  669. protected function getRefreshToken($refresh_token) {
  670. return NULL;
  671. }
  672. /**
  673. * Take the provided refresh token values and store them somewhere.
  674. *
  675. * This function should be the storage counterpart to getRefreshToken().
  676. *
  677. * If storage fails for some reason, we're not currently checking for
  678. * any sort of success/failure, so you should bail out of the script
  679. * and provide a descriptive fail message.
  680. *
  681. * Required for OAUTH2_GRANT_TYPE_REFRESH_TOKEN.
  682. *
  683. * @param $refresh_token
  684. * Refresh token to be stored.
  685. * @param $client_id
  686. * Client identifier to be stored.
  687. * @param $expires
  688. * expires to be stored.
  689. * @param $scope
  690. * (optional) Scopes to be stored in space-separated string.
  691. *
  692. * @ingroup oauth2_section_4
  693. */
  694. protected function setRefreshToken($refresh_token, $client_id, $expires, $scope = NULL) {
  695. return;
  696. }
  697. /**
  698. * Expire a used refresh token.
  699. *
  700. * This is not explicitly required in the spec, but is almost implied.
  701. * After granting a new refresh token, the old one is no longer useful and
  702. * so should be forcibly expired in the data store so it can't be used again.
  703. *
  704. * If storage fails for some reason, we're not currently checking for
  705. * any sort of success/failure, so you should bail out of the script
  706. * and provide a descriptive fail message.
  707. *
  708. * @param $refresh_token
  709. * Refresh token to be expirse.
  710. *
  711. * @ingroup oauth2_section_4
  712. */
  713. protected function unsetRefreshToken($refresh_token) {
  714. return;
  715. }
  716. /**
  717. * Grant access tokens for the "none" grant type.
  718. *
  719. * Not really described in the IETF Draft, so I just left a method
  720. * stub... Do whatever you want!
  721. *
  722. * Required for OAUTH2_GRANT_TYPE_NONE.
  723. *
  724. * @ingroup oauth2_section_4
  725. */
  726. protected function checkNoneAccess($client_id) {
  727. return FALSE;
  728. }
  729. /**
  730. * Get default authentication realm for WWW-Authenticate header.
  731. *
  732. * Change this to whatever authentication realm you want to send in a
  733. * WWW-Authenticate header.
  734. *
  735. * @return
  736. * A string that you want to send in a WWW-Authenticate header.
  737. *
  738. * @ingroup oauth2_error
  739. */
  740. protected function getDefaultAuthenticationRealm() {
  741. return "Service";
  742. }
  743. // End stuff that should get overridden.
  744. /**
  745. * Creates an OAuth2.0 server-side instance.
  746. *
  747. * @param $config
  748. * An associative array as below:
  749. * - access_token_lifetime: (optional) The lifetime of access token in
  750. * seconds.
  751. * - auth_code_lifetime: (optional) The lifetime of authorization code in
  752. * seconds.
  753. * - refresh_token_lifetime: (optional) The lifetime of refresh token in
  754. * seconds.
  755. * - display_error: (optional) Whether to show verbose error messages in
  756. * the response.
  757. */
  758. public function __construct($config = array()) {
  759. foreach ($config as $name => $value) {
  760. $this->setVariable($name, $value);
  761. }
  762. }
  763. // Resource protecting (Section 5).
  764. /**
  765. * Check that a valid access token has been provided.
  766. *
  767. * The scope parameter defines any required scope that the token must have.
  768. * If a scope param is provided and the token does not have the required
  769. * scope, we bounce the request.
  770. *
  771. * Some implementations may choose to return a subset of the protected
  772. * resource (i.e. "public" data) if the user has not provided an access
  773. * token or if the access token is invalid or expired.
  774. *
  775. * The IETF spec says that we should send a 401 Unauthorized header and
  776. * bail immediately so that's what the defaults are set to.
  777. *
  778. * @param $scope
  779. * A space-separated string of required scope(s), if you want to check
  780. * for scope.
  781. * @param $exit_not_present
  782. * If TRUE and no access token is provided, send a 401 header and exit,
  783. * otherwise return FALSE.
  784. * @param $exit_invalid
  785. * If TRUE and the implementation of getAccessToken() returns NULL, exit,
  786. * otherwise return FALSE.
  787. * @param $exit_expired
  788. * If TRUE and the access token has expired, exit, otherwise return FALSE.
  789. * @param $exit_scope
  790. * If TRUE the access token does not have the required scope(s), exit,
  791. * otherwise return FALSE.
  792. * @param $realm
  793. * If you want to specify a particular realm for the WWW-Authenticate
  794. * header, supply it here.
  795. *
  796. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5
  797. *
  798. * @ingroup oauth2_section_5
  799. */
  800. public function verifyAccessToken($scope = NULL, $exit_not_present = TRUE, $exit_invalid = TRUE, $exit_expired = TRUE, $exit_scope = TRUE, $realm = NULL) {
  801. $token_param = $this->getAccessTokenParams();
  802. if ($token_param === FALSE) // Access token was not provided
  803. return $exit_not_present ? $this->errorWWWAuthenticateResponseHeader(OAUTH2_HTTP_BAD_REQUEST, $realm, OAUTH2_ERROR_INVALID_REQUEST, 'The request is missing a required parameter, includes an unsupported parameter or parameter value, repeats the same parameter, uses more than one method for including an access token, or is otherwise malformed.', NULL, $scope) : FALSE;
  804. // Get the stored token data (from the implementing subclass)
  805. $token = $this->getAccessToken($token_param);
  806. if ($token === NULL)
  807. return $exit_invalid ? $this->errorWWWAuthenticateResponseHeader(OAUTH2_HTTP_UNAUTHORIZED, $realm, OAUTH2_ERROR_INVALID_TOKEN, 'The access token provided is invalid.', NULL, $scope) : FALSE;
  808. // Check token expiration (I'm leaving this check separated, later we'll fill in better error messages)
  809. if (isset($token["expires"]) && time() > $token["expires"])
  810. return $exit_expired ? $this->errorWWWAuthenticateResponseHeader(OAUTH2_HTTP_UNAUTHORIZED, $realm, OAUTH2_ERROR_EXPIRED_TOKEN, 'The access token provided has expired.', NULL, $scope) : FALSE;
  811. // Check scope, if provided
  812. // If token doesn't have a scope, it's NULL/empty, or it's insufficient, then throw an error
  813. if ($scope && (!isset($token["scope"]) || !$token["scope"] || !$this->checkScope($scope, $token["scope"])))
  814. return $exit_scope ? $this->errorWWWAuthenticateResponseHeader(OAUTH2_HTTP_FORBIDDEN, $realm, OAUTH2_ERROR_INSUFFICIENT_SCOPE, 'The request requires higher privileges than provided by the access token.', NULL, $scope) : FALSE;
  815. return TRUE;
  816. }
  817. /**
  818. * Check if everything in required scope is contained in available scope.
  819. *
  820. * @param $required_scope
  821. * Required scope to be check with.
  822. * @param $available_scope
  823. * Available scope to be compare with.
  824. *
  825. * @return
  826. * TRUE if everything in required scope is contained in available scope,
  827. * and False if it isn't.
  828. *
  829. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5
  830. *
  831. * @ingroup oauth2_section_5
  832. */
  833. private function checkScope($required_scope, $available_scope) {
  834. // The required scope should match or be a subset of the available scope
  835. if (!is_array($required_scope))
  836. $required_scope = explode(" ", $required_scope);
  837. if (!is_array($available_scope))
  838. $available_scope = explode(" ", $available_scope);
  839. return (count(array_diff($required_scope, $available_scope)) == 0);
  840. }
  841. /**
  842. * Pulls the access token out of the HTTP request.
  843. *
  844. * Either from the Authorization header or GET/POST/etc.
  845. *
  846. * @return
  847. * Access token value if present, and FALSE if it isn't.
  848. *
  849. * @todo Support PUT or DELETE.
  850. *
  851. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5.1
  852. *
  853. * @ingroup oauth2_section_5
  854. */
  855. private function getAccessTokenParams() {
  856. $auth_header = $this->getAuthorizationHeader();
  857. if ($auth_header !== FALSE) {
  858. // Make sure only the auth header is set
  859. if (isset($_GET[OAUTH2_TOKEN_PARAM_NAME]) || isset($_POST[OAUTH2_TOKEN_PARAM_NAME]))
  860. $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST, 'Auth token found in GET or POST when token present in header');
  861. $auth_header = trim($auth_header);
  862. // Make sure it's Token authorization
  863. if (strcmp(substr($auth_header, 0, 5), "OAuth ") !== 0)
  864. $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST, 'Auth header found that doesn\'t start with "OAuth"');
  865. // Parse the rest of the header
  866. if (preg_match('/\s*OAuth\s*="(.+)"/', substr($auth_header, 5), $matches) == 0 || count($matches) < 2)
  867. $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST, 'Malformed auth header');
  868. return $matches[1];
  869. }
  870. if (isset($_GET[OAUTH2_TOKEN_PARAM_NAME])) {
  871. if (isset($_POST[OAUTH2_TOKEN_PARAM_NAME])) // Both GET and POST are not allowed
  872. $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST, 'Only send the token in GET or POST, not both');
  873. return $_GET[OAUTH2_TOKEN_PARAM_NAME];
  874. }
  875. if (isset($_POST[OAUTH2_TOKEN_PARAM_NAME]))
  876. return $_POST[OAUTH2_TOKEN_PARAM_NAME];
  877. return FALSE;
  878. }
  879. // Access token granting (Section 4).
  880. /**
  881. * Grant or deny a requested access token.
  882. *
  883. * This would be called from the "/token" endpoint as defined in the spec.
  884. * Obviously, you can call your endpoint whatever you want.
  885. *
  886. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4
  887. *
  888. * @ingroup oauth2_section_4
  889. */
  890. public function grantAccessToken() {
  891. $filters = array(
  892. "grant_type" => array("filter" => FILTER_VALIDATE_REGEXP, "options" => array("regexp" => OAUTH2_GRANT_TYPE_REGEXP), "flags" => FILTER_REQUIRE_SCALAR),
  893. "scope" => array("flags" => FILTER_REQUIRE_SCALAR),
  894. "code" => array("flags" => FILTER_REQUIRE_SCALAR),
  895. "redirect_uri" => array("filter" => FILTER_SANITIZE_URL),
  896. "username" => array("flags" => FILTER_REQUIRE_SCALAR),
  897. "password" => array("flags" => FILTER_REQUIRE_SCALAR),
  898. "assertion_type" => array("flags" => FILTER_REQUIRE_SCALAR),
  899. "assertion" => array("flags" => FILTER_REQUIRE_SCALAR),
  900. "refresh_token" => array("flags" => FILTER_REQUIRE_SCALAR),
  901. );
  902. $input = filter_input_array(INPUT_POST, $filters);
  903. // Grant Type must be specified.
  904. if (!$input["grant_type"])
  905. $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST, 'Invalid grant_type parameter or parameter missing');
  906. // Make sure we've implemented the requested grant type
  907. if (!in_array($input["grant_type"], $this->getSupportedGrantTypes()))
  908. $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_UNSUPPORTED_GRANT_TYPE);
  909. // Authorize the client
  910. $client = $this->getClientCredentials();
  911. if ($this->checkClientCredentials($client[0], $client[1]) === FALSE)
  912. $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_CLIENT);
  913. if (!$this->checkRestrictedGrantType($client[0], $input["grant_type"]))
  914. $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_UNAUTHORIZED_CLIENT);
  915. // Do the granting
  916. switch ($input["grant_type"]) {
  917. case OAUTH2_GRANT_TYPE_AUTH_CODE:
  918. if (!$input["code"] || !$input["redirect_uri"])
  919. $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST);
  920. $stored = $this->getAuthCode($input["code"]);
  921. // Ensure that the input uri starts with the stored uri
  922. if ($stored === NULL || (strcasecmp(substr($input["redirect_uri"], 0, strlen($stored["redirect_uri"])), $stored["redirect_uri"]) !== 0) || $client[0] != $stored["client_id"])
  923. $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_GRANT);
  924. if ($stored["expires"] < time())
  925. $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_EXPIRED_TOKEN);
  926. break;
  927. case OAUTH2_GRANT_TYPE_USER_CREDENTIALS:
  928. if (!$input["username"] || !$input["password"])
  929. $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST, 'Missing parameters. "username" and "password" required');
  930. $stored = $this->checkUserCredentials($client[0], $input["username"], $input["password"]);
  931. if ($stored === FALSE)
  932. $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_GRANT);
  933. break;
  934. case OAUTH2_GRANT_TYPE_ASSERTION:
  935. if (!$input["assertion_type"] || !$input["assertion"])
  936. $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST);
  937. $stored = $this->checkAssertion($client[0], $input["assertion_type"], $input["assertion"]);
  938. if ($stored === FALSE)
  939. $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_GRANT);
  940. break;
  941. case OAUTH2_GRANT_TYPE_REFRESH_TOKEN:
  942. if (!$input["refresh_token"])
  943. $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST, 'No "refresh_token" parameter found');
  944. $stored = $this->getRefreshToken($input["refresh_token"]);
  945. if ($stored === NULL || $client[0] != $stored["client_id"])
  946. $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_GRANT);
  947. if ($stored["expires"] < time())
  948. $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_EXPIRED_TOKEN);
  949. // store the refresh token locally so we can delete it when a new refresh token is generated
  950. $this->setVariable('_old_refresh_token', $stored["token"]);
  951. break;
  952. case OAUTH2_GRANT_TYPE_NONE:
  953. $stored = $this->checkNoneAccess($client[0]);
  954. if ($stored === FALSE)
  955. $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_REQUEST);
  956. }
  957. // Check scope, if provided
  958. if ($input["scope"] && (!is_array($stored) || !isset($stored["scope"]) || !$this->checkScope($input["scope"], $stored["scope"])))
  959. $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_SCOPE);
  960. if (!$input["scope"])
  961. $input["scope"] = NULL;
  962. $token = $this->createAccessToken($client[0], $input["scope"], $stored["user_id"]);
  963. //TOKEN 为 数组 (包括 TOKEN 值, 有效时间, 权限)
  964. // $this->sendJsonHeaders();
  965. echo json_encode($token);
  966. }
  967. /**
  968. * Internal function used to get the client credentials from HTTP basic
  969. * auth or POST data.
  970. *
  971. * @return
  972. * A list containing the client identifier and password, for example
  973. * @code
  974. * return array(
  975. * $_POST["client_id"],
  976. * $_POST["client_secret"],
  977. * );
  978. * @endcode
  979. *
  980. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-2
  981. *
  982. * @ingroup oauth2_section_2
  983. */
  984. protected function getClientCredentials() {
  985. if (isset($_SERVER["PHP_AUTH_USER"]) && $_POST && isset($_POST["client_id"]))
  986. $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_CLIENT);
  987. // Try basic auth
  988. if (isset($_SERVER["PHP_AUTH_USER"]))
  989. return array($_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"]);
  990. // Try POST
  991. if ($_POST && isset($_POST["client_id"])) {
  992. if (isset($_POST["client_secret"]))
  993. return array($_POST["client_id"], $_POST["client_secret"]);
  994. return array($_POST["client_id"], NULL);
  995. }
  996. // No credentials were specified
  997. $this->errorJsonResponse(OAUTH2_HTTP_BAD_REQUEST, OAUTH2_ERROR_INVALID_CLIENT);
  998. }
  999. // End-user/client Authorization (Section 3 of IETF Draft).
  1000. /**
  1001. * Pull the authorization request data out of the HTTP request.
  1002. *
  1003. * @return
  1004. * The authorization parameters so the authorization server can prompt
  1005. * the user for approval if valid.
  1006. *
  1007. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-3
  1008. *
  1009. * @ingroup oauth2_section_3
  1010. */
  1011. public function getAuthorizeParams() {
  1012. $filters = array(
  1013. "client_id" => array("filter" => FILTER_VALIDATE_REGEXP, "options" => array("regexp" => OAUTH2_CLIENT_ID_REGEXP), "flags" => FILTER_REQUIRE_SCALAR),
  1014. "response_type" => array("filter" => FILTER_VALIDATE_REGEXP, "options" => array("regexp" => OAUTH2_AUTH_RESPONSE_TYPE_REGEXP), "flags" => FILTER_REQUIRE_SCALAR),
  1015. "redirect_uri" => array("filter" => FILTER_SANITIZE_URL),
  1016. "state" => array("flags" => FILTER_REQUIRE_SCALAR),
  1017. "scope" => array("flags" => FILTER_REQUIRE_SCALAR),
  1018. );
  1019. $input = filter_input_array(INPUT_GET, $filters);
  1020. // Make sure a valid client id was supplied
  1021. if (!$input["client_id"]) {
  1022. if ($input["redirect_uri"])
  1023. $this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_INVALID_CLIENT, NULL, NULL, $input["state"]);
  1024. $this->errorJsonResponse(OAUTH2_HTTP_FOUND, OAUTH2_ERROR_INVALID_CLIENT); // We don't have a good URI to use
  1025. }
  1026. // redirect_uri is not required if already established via other channels
  1027. // check an existing redirect URI against the one supplied
  1028. $redirect_uri = $this->getRedirectUri($input["client_id"]);
  1029. // At least one of: existing redirect URI or input redirect URI must be specified
  1030. if (!$redirect_uri && !$input["redirect_uri"])
  1031. $this->errorJsonResponse(OAUTH2_HTTP_FOUND, OAUTH2_ERROR_INVALID_REQUEST);
  1032. // getRedirectUri() should return FALSE if the given client ID is invalid
  1033. // this probably saves us from making a separate db call, and simplifies the method set
  1034. if ($redirect_uri === FALSE)
  1035. $this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_INVALID_CLIENT, NULL, NULL, $input["state"]);
  1036. // If there's an existing uri and one from input, verify that they match
  1037. if ($redirect_uri && $input["redirect_uri"]) {
  1038. // Ensure that the input uri starts with the stored uri
  1039. if (strcasecmp(substr($input["redirect_uri"], 0, strlen($redirect_uri)), $redirect_uri) !== 0)
  1040. $this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_REDIRECT_URI_MISMATCH, NULL, NULL, $input["state"]);
  1041. }
  1042. elseif ($redirect_uri) { // They did not provide a uri from input, so use the stored one
  1043. $input["redirect_uri"] = $redirect_uri;
  1044. }
  1045. // type and client_id are required
  1046. if (!$input["response_type"])
  1047. $this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_INVALID_REQUEST, 'Invalid response type.', NULL, $input["state"]);
  1048. // Check requested auth response type against the list of supported types
  1049. if (array_search($input["response_type"], $this->getSupportedAuthResponseTypes()) === FALSE)
  1050. $this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_UNSUPPORTED_RESPONSE_TYPE, NULL, NULL, $input["state"]);
  1051. // Restrict clients to certain authorization response types
  1052. if ($this->checkRestrictedAuthResponseType($input["client_id"], $input["response_type"]) === FALSE)
  1053. $this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_UNAUTHORIZED_CLIENT, NULL, NULL, $input["state"]);
  1054. // Validate that the requested scope is supported
  1055. if ($input["scope"] && !$this->checkScope($input["scope"], $this->getSupportedScopes()))
  1056. $this->errorDoRedirectUriCallback($input["redirect_uri"], OAUTH2_ERROR_INVALID_SCOPE, NULL, NULL, $input["state"]);
  1057. return $input;
  1058. }
  1059. /**
  1060. * Redirect the user appropriately after approval.
  1061. *
  1062. * After the user has approved or denied the access request the
  1063. * authorization server should call this function to redirect the user
  1064. * appropriately.
  1065. *
  1066. * @param $is_authorized
  1067. * TRUE or FALSE depending on whether the user authorized the access.
  1068. * @param $params
  1069. * An associative array as below:
  1070. * - response_type: The requested response: an access token, an
  1071. * authorization code, or both.
  1072. * - client_id: The client identifier as described in Section 2.
  1073. * - redirect_uri: An absolute URI to which the authorization server
  1074. * will redirect the user-agent to when the end-user authorization
  1075. * step is completed.
  1076. * - scope: (optional) The scope of the access request expressed as a
  1077. * list of space-delimited strings.
  1078. * - state: (optional) An opaque value used by the client to maintain
  1079. * state between the request and callback.
  1080. *
  1081. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-3
  1082. *
  1083. * @ingroup oauth2_section_3
  1084. */
  1085. public function finishClientAuthorization($is_authorized, $params = array()) {
  1086. $params += array(
  1087. // 'scope' => NULL,
  1088. 'state' => NULL,
  1089. );
  1090. extract($params);
  1091. if ($state !== NULL)
  1092. $result["query"]["state"] = $state;
  1093. if ($is_authorized === FALSE) {
  1094. $result["query"]["error"] = OAUTH2_ERROR_USER_DENIED;
  1095. }
  1096. else {
  1097. if ($response_type == OAUTH2_AUTH_RESPONSE_TYPE_AUTH_CODE || $response_type == OAUTH2_AUTH_RESPONSE_TYPE_CODE_AND_TOKEN)
  1098. $result["query"]["code"] = $this->createAuthCode($client_id, $redirect_uri, $user_id, $scope);
  1099. if ($response_type == OAUTH2_AUTH_RESPONSE_TYPE_ACCESS_TOKEN || $response_type == OAUTH2_AUTH_RESPONSE_TYPE_CODE_AND_TOKEN)
  1100. $result["fragment"] = $this->createAccessToken($client_id, $scope, $user_id);
  1101. }
  1102. $result['display'] = !empty($display) ? $display : '';
  1103. $this->doRedirectUriCallback($redirect_uri, $result);
  1104. }
  1105. // Other/utility functions.
  1106. /**
  1107. * Redirect the user agent.
  1108. *
  1109. * Handle both redirect for success or error response.
  1110. *
  1111. * @param $redirect_uri
  1112. * An absolute URI to which the authorization server will redirect
  1113. * the user-agent to when the end-user authorization step is completed.
  1114. * @param $params
  1115. * Parameters to be pass though buildUri().
  1116. *
  1117. * @ingroup oauth2_section_3
  1118. */
  1119. private function doRedirectUriCallback($redirect_uri, $params) {
  1120. header("HTTP/1.1 ". OAUTH2_HTTP_FOUND);
  1121. if($params['display'] == 'frame') {
  1122. echo '<script>window.top.location.href="'.$this->buildUri($redirect_uri, $params).'";</script>';
  1123. } else {
  1124. header("Location: " . $this->buildUri($redirect_uri, $params));
  1125. }
  1126. exit;
  1127. }
  1128. /**
  1129. * Build the absolute URI based on supplied URI and parameters.
  1130. *
  1131. * @param $uri
  1132. * An absolute URI.
  1133. * @param $params
  1134. * Parameters to be append as GET.
  1135. *
  1136. * @return
  1137. * An absolute URI with supplied parameters.
  1138. *
  1139. * @ingroup oauth2_section_3
  1140. */
  1141. private function buildUri($uri, $params) {
  1142. session_start();
  1143. $parse_url = parse_url($uri);
  1144. // Add our params to the parsed uri
  1145. foreach ($params as $k => $v) {
  1146. if (isset($parse_url[$k]))
  1147. $parse_url[$k] .= "&" . http_build_query($v);
  1148. else
  1149. $parse_url[$k] = http_build_query($v);
  1150. }
  1151. // Put humpty dumpty back together
  1152. $return =
  1153. ((isset($parse_url["scheme"])) ? $parse_url["scheme"] . "://" : "")
  1154. . ((isset($parse_url["user"])) ? $parse_url["user"] . ((isset($parse_url["pass"])) ? ":" . $parse_url["pass"] : "") . "@" : "")
  1155. . ((isset($parse_url["host"])) ? $parse_url["host"] : "")
  1156. . ((isset($parse_url["port"])) ? ":" . $parse_url["port"] : "")
  1157. . ((isset($parse_url["path"])) ? $parse_url["path"] : "")
  1158. . ((isset($parse_url["query"])) ? "?" . $parse_url["query"] : "")
  1159. . ((isset($parse_url["fragment"])) ? "#" . $parse_url["fragment"] : "") . "&ssid=".base64_encode('2egc83'.session_id());
  1160. return $return;
  1161. }
  1162. /**
  1163. * Handle the creation of access token, also issue refresh token if support.
  1164. *
  1165. * This belongs in a separate factory, but to keep it simple, I'm just
  1166. * keeping it here.
  1167. *
  1168. * @param $client_id
  1169. * Client identifier related to the access token.
  1170. * @param $scope
  1171. * (optional) Scopes to be stored in space-separated string.
  1172. *
  1173. * @ingroup oauth2_section_4
  1174. */
  1175. protected function createAccessToken($client_id, $scope = NULL, $user_id = 10) {
  1176. $token = array(
  1177. "access_token" => $this->genAccessToken(),
  1178. "expires_in" => $this->getVariable('access_token_lifetime', OAUTH2_DEFAULT_ACCESS_TOKEN_LIFETIME),
  1179. "user_id" => $user_id,
  1180. "scope" => $scope
  1181. );
  1182. $refresh_token = '';
  1183. // Issue a refresh token also, if we support them
  1184. if (in_array(OAUTH2_GRANT_TYPE_REFRESH_TOKEN, $this->getSupportedGrantTypes())) {
  1185. $refresh_token = $this->genAccessToken();
  1186. // $this->setRefreshToken($token["refresh_token"], $client_id, time() + $this->getVariable('refresh_token_lifetime', OAUTH2_DEFAULT_REFRESH_TOKEN_LIFETIME), $user_id, $scope);
  1187. $token["refresh_token"] = $refresh_token;
  1188. // If we've granted a new refresh token, expire the old one
  1189. if ($this->getVariable('_old_refresh_token'))
  1190. $this->unsetRefreshToken($this->getVariable('_old_refresh_token'));
  1191. }
  1192. $access_token = $token['access_token'];
  1193. $this->setAccessToken($access_token, $client_id, time() + $this->getVariable('access_token_lifetime', OAUTH2_DEFAULT_ACCESS_TOKEN_LIFETIME), $token['user_id'], $scope, $refresh_token);
  1194. return $token;
  1195. }
  1196. /**
  1197. * Handle the creation of auth code.
  1198. *
  1199. * This belongs in a separate factory, but to keep it simple, I'm just
  1200. * keeping it here.
  1201. *
  1202. * @param $client_id
  1203. * Client identifier related to the access token.
  1204. * @param $redirect_uri
  1205. * An absolute URI to which the authorization server will redirect the
  1206. * user-agent to when the end-user authorization step is completed.
  1207. * @param $scope
  1208. * (optional) Scopes to be stored in space-separated string.
  1209. *
  1210. * @ingroup oauth2_section_3
  1211. */
  1212. private function createAuthCode($client_id, $redirect_uri, $userId, $scope = NULL) {
  1213. $code = $this->genAuthCode();
  1214. $this->setAuthCode($code, $client_id, $redirect_uri, time() + $this->getVariable('auth_code_lifetime', OAUTH2_DEFAULT_AUTH_CODE_LIFETIME), $userId, $scope);
  1215. return $code;
  1216. }
  1217. /**
  1218. * Generate unique access token.
  1219. *
  1220. * Implementing classes may want to override these function to implement
  1221. * other access token or auth code generation schemes.
  1222. *
  1223. * @return
  1224. * An unique access token.
  1225. *
  1226. * @ingroup oauth2_section_4
  1227. */
  1228. protected function genAccessToken() {
  1229. return md5(base64_encode(pack('N6', mt_rand(), mt_rand(), mt_rand(), mt_rand(), mt_rand(), uniqid())));
  1230. }
  1231. /**
  1232. * Generate unique auth code.
  1233. *
  1234. * Implementing classes may want to override these function to implement
  1235. * other access token or auth code generation schemes.
  1236. *
  1237. * @return
  1238. * An unique auth code.
  1239. *
  1240. * @ingroup oauth2_section_3
  1241. */
  1242. protected function genAuthCode() {
  1243. return md5(base64_encode(pack('N6', mt_rand(), mt_rand(), mt_rand(), mt_rand(), mt_rand(), uniqid())));
  1244. }
  1245. /**
  1246. * Pull out the Authorization HTTP header and return it.
  1247. *
  1248. * Implementing classes may need to override this function for use on
  1249. * non-Apache web servers.
  1250. *
  1251. * @return
  1252. * The Authorization HTTP header, and FALSE if does not exist.
  1253. *
  1254. * @todo Handle Authorization HTTP header for non-Apache web servers.
  1255. *
  1256. * @ingroup oauth2_section_5
  1257. */
  1258. private function getAuthorizationHeader() {
  1259. if (array_key_exists("HTTP_AUTHORIZATION", $_SERVER))
  1260. return $_SERVER["HTTP_AUTHORIZATION"];
  1261. if (function_exists("apache_request_headers")) {
  1262. $headers = apache_request_headers();
  1263. if (array_key_exists("Authorization", $headers))
  1264. return $headers["Authorization"];
  1265. }
  1266. return FALSE;
  1267. }
  1268. /**
  1269. * Send out HTTP headers for JSON.
  1270. *
  1271. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.2
  1272. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.3
  1273. *
  1274. * @ingroup oauth2_section_4
  1275. */
  1276. private function sendJsonHeaders() {
  1277. header("Content-Type: application/json");
  1278. header("Cache-Control: no-store");
  1279. }
  1280. /**
  1281. * Redirect the end-user's user agent with error message.
  1282. *
  1283. * @param $redirect_uri
  1284. * An absolute URI to which the authorization server will redirect the
  1285. * user-agent to when the end-user authorization step is completed.
  1286. * @param $error
  1287. * A single error code as described in Section 3.2.1.
  1288. * @param $error_description
  1289. * (optional) A human-readable text providing additional information,
  1290. * used to assist in the understanding and resolution of the error
  1291. * occurred.
  1292. * @param $error_uri
  1293. * (optional) A URI identifying a human-readable web page with
  1294. * information about the error, used to provide the end-user with
  1295. * additional information about the error.
  1296. * @param $state
  1297. * (optional) REQUIRED if the "state" parameter was present in the client
  1298. * authorization request. Set to the exact value received from the client.
  1299. *
  1300. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-3.2
  1301. *
  1302. * @ingroup oauth2_error
  1303. */
  1304. private function errorDoRedirectUriCallback($redirect_uri, $error, $error_description = NULL, $error_uri = NULL, $state = NULL) {
  1305. $result["query"]["error"] = $error;
  1306. if ($state)
  1307. $result["query"]["state"] = $state;
  1308. if ($this->getVariable('display_error') && $error_description)
  1309. $result["query"]["error_description"] = $error_description;
  1310. if ($this->getVariable('display_error') && $error_uri)
  1311. $result["query"]["error_uri"] = $error_uri;
  1312. $this->doRedirectUriCallback($redirect_uri, $result);
  1313. }
  1314. /**
  1315. * Send out error message in JSON.
  1316. *
  1317. * @param $http_status_code
  1318. * HTTP status code message as predefined.
  1319. * @param $error
  1320. * A single error code.
  1321. * @param $error_description
  1322. * (optional) A human-readable text providing additional information,
  1323. * used to assist in the understanding and resolution of the error
  1324. * occurred.
  1325. * @param $error_uri
  1326. * (optional) A URI identifying a human-readable web page with
  1327. * information about the error, used to provide the end-user with
  1328. * additional information about the error.
  1329. *
  1330. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.3
  1331. *
  1332. * @ingroup oauth2_error
  1333. */
  1334. private function errorJsonResponse($http_status_code, $error, $error_description = NULL, $error_uri = NULL) {
  1335. $result['error'] = $error;
  1336. if ($this->getVariable('display_error') && $error_description)
  1337. $result["error_description"] = $error_description;
  1338. if ($this->getVariable('display_error') && $error_uri)
  1339. $result["error_uri"] = $error_uri;
  1340. header("HTTP/1.1 " . $http_status_code);
  1341. $this->sendJsonHeaders();
  1342. echo json_encode($result);
  1343. exit;
  1344. }
  1345. /**
  1346. * Send a 401 unauthorized header with the given realm and an error, if
  1347. * provided.
  1348. *
  1349. * @param $http_status_code
  1350. * HTTP status code message as predefined.
  1351. * @param $realm
  1352. * The "realm" attribute is used to provide the protected resources
  1353. * partition as defined by [RFC2617].
  1354. * @param $scope
  1355. * A space-delimited list of scope values indicating the required scope
  1356. * of the access token for accessing the requested resource.
  1357. * @param $error
  1358. * The "error" attribute is used to provide the client with the reason
  1359. * why the access request was declined.
  1360. * @param $error_description
  1361. * (optional) The "error_description" attribute provides a human-readable text
  1362. * containing additional information, used to assist in the understanding
  1363. * and resolution of the error occurred.
  1364. * @param $error_uri
  1365. * (optional) The "error_uri" attribute provides a URI identifying a human-readable
  1366. * web page with information about the error, used to offer the end-user
  1367. * with additional information about the error. If the value is not an
  1368. * absolute URI, it is relative to the URI of the requested protected
  1369. * resource.
  1370. *
  1371. * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-5.2
  1372. *
  1373. * @ingroup oauth2_error
  1374. */
  1375. private function errorWWWAuthenticateResponseHeader($http_status_code, $realm, $error, $error_description = NULL, $error_uri = NULL, $scope = NULL) {
  1376. $realm = $realm === NULL ? $this->getDefaultAuthenticationRealm() : $realm;
  1377. $result = "WWW-Authenticate: OAuth realm='" . $realm . "'";
  1378. if ($error)
  1379. $result .= ", error='" . $error . "'";
  1380. if ($this->getVariable('display_error') && $error_description)
  1381. $result .= ", error_description='" . $error_description . "'";
  1382. if ($this->getVariable('display_error') && $error_uri)
  1383. $result .= ", error_uri='" . $error_uri . "'";
  1384. if ($scope)
  1385. $result .= ", scope='" . $scope . "'";
  1386. header("HTTP/1.1 ". $http_status_code);
  1387. header($result);
  1388. exit;
  1389. }
  1390. }

代码比较多。有兴趣的可以细看。

由于采用Vendor方法引入,所以这里的文件都没有采用命名空间。

5-3、在同目录下,建ThinkOAuth2.php文件,这个文件里的类继承上面那个基类:

  1. <?php
  2. require "oauth2.class.php";
  3. class ThinkOAuth2 extends OAuth2{
  4. private $db;
  5. private $table;
  6. /**
  7. * 构造
  8. */
  9. public function __construct() {
  10. parent::__construct();
  11. $this -> db = M();
  12. $this -> table = array(
  13. 'auth_codes'=>C('OAUTH2_CODES_TABLE'),
  14. 'clients'=>C('OAUTH2_CLIENTS_TABLE'),
  15. 'tokens'=>C('OAUTH2_TOKEN_TABLE')
  16. );
  17. }
  18. /**
  19. * 析构
  20. */
  21. function __destruct() {
  22. $this->db = NULL; // Release db connection
  23. }
  24. private function handleException($e) {
  25. echo "Database error: " . $e->getMessage();
  26. exit;
  27. }
  28. /**
  29. *
  30. * 增加client
  31. * @param string $client_id
  32. * @param string $client_secret
  33. * @param string $redirect_uri
  34. */
  35. public function addClient($client_id, $client_secret, $redirect_uri) {
  36. $time = time();
  37. $sql = "INSERT INTO {$this -> table['clients']} (client_id, client_secret, redirect_uri,create_time) VALUES ('{$client_id}', '{$client_secret}', '{$redirect_uri}','{$time}')";
  38. $res = $this -> db -> execute($sql);
  39. return res;
  40. }
  41. /**
  42. * Implements OAuth2::checkClientCredentials()
  43. * @see OAuth2::checkClientCredentials()
  44. */
  45. protected function checkClientCredentials($client_id, $client_secret = NULL) {
  46. $sql = "SELECT client_secret FROM {$this -> table['clients']} WHERE client_id = {$client_id}";
  47. $result = $this -> db -> query($sql);
  48. if ($client_secret === NULL) {
  49. return $result !== FALSE;
  50. }
  51. //Log::write("checkClientCredentials : ".$result);
  52. //Log::write("checkClientCredentials : ".$result[0]);
  53. //Log::write("checkClientCredentials : ".$result[0]["client_secret"]);
  54. return $result[0]["client_secret"] == $client_secret;
  55. }
  56. /**
  57. * Implements OAuth2::getRedirectUri().
  58. * @see OAuth2::getRedirectUri()
  59. */
  60. protected function getRedirectUri($client_id) {
  61. $sql = "SELECT redirect_uri FROM {$this -> table['clients']} WHERE client_id = {$client_id}";
  62. $result = $this -> db -> query($sql);
  63. if ($result === FALSE) {
  64. return FALSE;
  65. }
  66. //Log::write("getRedirectUri : ".$result);
  67. //Log::write("getRedirectUri : ".$result[0]);
  68. //Log::write("getRedirectUri : ".$result[0]["redirect_uri"]);
  69. return isset($result[0]["redirect_uri"]) && $result[0]["redirect_uri"] ? $result[0]["redirect_uri"] : NULL;
  70. }
  71. /**
  72. * Implements OAuth2::getAccessToken().
  73. * @see OAuth2::getAccessToken()
  74. */
  75. protected function getAccessToken($access_token) {
  76. $sql = "SELECT client_id, expires_in, scope FROM {$this -> table['tokens']} WHERE access_token = '{$access_token}'";
  77. $result = $this -> db -> query($sql);
  78. //Log::write("getAccessToken : ".$result);
  79. //Log::write("getAccessToken : ".$result[0]);
  80. return $result !== FALSE ? $result : NULL;
  81. }
  82. /**
  83. * Implements OAuth2::setAccessToken().
  84. * @see OAuth2::setAccessToken()
  85. */
  86. protected function setAccessToken($access_token, $client_id, $expires, $user_id, $scope=NULL, $refresh_token='') {
  87. $sql = "INSERT INTO {$this -> table['tokens']} (access_token, client_id, expires_in, scope,user_id,refresh_token) VALUES ('{$access_token}', '{$client_id}', '{$expires}', '{$scope}',{$user_id},'{$refresh_token}')";
  88. $this -> db -> execute($sql);
  89. }
  90. /**
  91. * Overrides OAuth2::getSupportedGrantTypes().
  92. * @see OAuth2::getSupportedGrantTypes()
  93. */
  94. protected function getSupportedGrantTypes() {
  95. return array(
  96. OAUTH2_GRANT_TYPE_AUTH_CODE
  97. );
  98. }
  99. /**
  100. * Overrides OAuth2::getAuthCode().
  101. * @see OAuth2::getAuthCode()
  102. */
  103. protected function getAuthCode($code) {
  104. $sql = "SELECT user_id, code, client_id, redirect_uri, expires, scope FROM {$this -> table['auth_codes']} WHERE code = '{$code}'";
  105. $result = $this -> db -> query($sql);
  106. //Log::write("getAuthcode : ".$result);
  107. //Log::write("getAuthcode : ".$result[0]);
  108. //Log::write("getAuthcode : ".$result[0]["code"]);
  109. return $result !== FALSE ? $result[0] : NULL;
  110. }
  111. /**
  112. * Overrides OAuth2::setAuthCode().
  113. * @see OAuth2::setAuthCode()
  114. */
  115. protected function setAuthCode($code, $client_id, $redirect_uri, $expires, $scope = NULL) {
  116. $time = time();
  117. $sql = "INSERT INTO {$this -> table['auth_codes']} (code, client_id, redirect_uri, expires, scope) VALUES ('{$code}', '{$client_id}', '{$redirect_uri}', '{$expires}', '{$scope}')";
  118. $result = $this -> db -> execute($sql);
  119. }
  120. /**
  121. * Overrides OAuth2::checkUserCredentials().
  122. * @see OAuth2::checkUserCredentials()
  123. */
  124. protected function checkUserCredentials($client_id, $username, $password){
  125. return TRUE;
  126. }
  127. }

这里涉及到一些数据库的操作。继承出来操作,还是比较一目了然的。

5-4、准备好类了。我们就可以实现控制器里的功能代码。动手之前先搞清楚我们的意图。

首先,我们需要分配一个oauth_client和一个oauth_secrete给第三方网站(也就是client.ruanwenwu.cn)来获取oauth服务。

其次,client.ruanwenwu.cn会把用户导入到server.ruanwenwu.cn来进行授权。所以我们我们还需要准备授权页面。

因为涉及到服务端和客户端的数据交互,所以可能服务端和客户端的相关介绍我会交叉进行。

5-4-1、创建oauth_client。

5-4-2、获取oauth_cliest列表。

5-4-3、准备获取用户授权的页面和逻辑。(授权服务器)

5-4-4、准备提供用户数据的逻辑。(资源服务器)

这些逻辑我都写在一个控制器OauthController.class.php里。代码如下:

  1. <?php
  2. namespace Home\Controller;
  3. use Think\Controller;
  4. class OauthController extends Controller{
  5. private $oauth = NULL;
  6. function _initialize(){
  7. header("Content-Type: application/json");
  8. Vendor("oauth.ThinkOAuth2");
  9. $this -> oauth = new \ThinkOAuth2();
  10. }
  11. public function index(){
  12. header("Content-Type:application/json; charset=utf-8");
  13. $this -> ajaxReturn(null, 'oauth-server-start', 1, 'json');
  14. }
  15. public function access_token() {
  16. $this -> oauth -> grantAccessToken();
  17. }
  18. //权限验证
  19. public function authorize() {
  20. if (IS_POST) {
  21. if(true){
  22. //这个地方验证成功后要把用户的uid加进来
  23. $_POST['user_id'] = 1;
  24. $this -> oauth -> finishClientAuthorization(true, $_POST); //第一个参数很重要,是用户是否授权
  25. }
  26. return;
  27. }
  28. ///表单准备
  29. $auth_params = $this -> oauth -> getAuthorizeParams();
  30. $this->assign("auth_params",$auth_params);
  31. $this->display();
  32. }
  33. public function addclient() {
  34. echo 'jack';
  35. if (IS_POST) {
  36. echo 'pd';
  37. $res = $this -> oauth -> addClient($_POST["client_id"], $_POST["client_secret"], $_POST["redirect_uri"]);
  38. if($res){
  39. $this->redirect("/home/Oauth/clientlist");
  40. die;
  41. }
  42. }
  43. $this->display();
  44. }
  45. public function clientlist(){
  46. $res = M()->table("oauth_client")->select();
  47. $this->assign("clients",$res);
  48. var_dump($res);
  49. $this->display();
  50. }
  51. }

顺便我将用到的模板文件(server.ruanwenwu.cn\Application\Home\View\oauth目录下)也贴出来给大家参考一下:

addclient.html

  1. <!doctype html>
  2. <head>
  3. <meta charset="utf-8" />
  4. <title>这里是添加客户端界面</title>
  5. </head>
  6. <body>
  7. <h1>添加客户端</h1>
  8. <form action="" method="post" >
  9. clientId:<input type="text" name="client_id" />
  10. <br />
  11. client_secrete:<input type="text" name="client_secret" />
  12. <br />
  13. callbackUrl:<input type="text" name="redirect_uri" />
  14. <br />
  15. <input type="submit" value="submit" />
  16. </form>
  17. </body>
  18. </html>

authorize.html

  1. <!doctype html>
  2. <html>
  3. <form action="" method="post">
  4. 用户名:<input type="text" name="username" />
  5. <br />
  6. 密码:<input type="text" name="pwd" />
  7. <input type="hidden" name="response_type" value="code" />
  8. <input type="hidden" name="client_id" value="{$auth_params['client_id']}" />
  9. <input type="hidden" name="redirect_uri" value="{$auth_params['redirect_uri']}" />
  10. <input type="submit" value="submit" />
  11. </form>
  12. </html>

注意:我这里用到了几个隐藏域。这个是必须的,在post提交验证时会用到。

5-4-5、创建oauth2.0用户。

打 开server.ruanwenwu.cn/index.php/home/oauth/addcient,输入client_id和 client_secrete还有redirect_uri创建一个oauth2.0用户。其中redirect_uri是用户授权后需要跳转的地方。

到这里服务端的部署就差不多结束了。现在开始部署客户端。

三、oauth2.0客户端搭建。

1、同服务端一样搭建client.ruanwenwu.cn。(也是用thinkphp)。

2、能正常访问后。在类库中加入oauth2.0客户端类。

在\client.ruanwenwu.cn\ThinkPHP\Library\Org\Util目录下建立Oauth2.class.php,代码如下:

  1. <?php
  2. namespace Org\Util;
  3. /**
  4. * 与 OAuth2 服务器 通讯验证 核心类 (PHP版)
  5. *
  6. * @description OAuth2 说明请大家参考 木蚂蚁接口文档
  7. *
  8. *
  9. * @author Mumayi-Team (zhaopan赵攀)
  10. * @version 1.0
  11. * @datetime 2013-12-31
  12. */
  13. class Oauth2
  14. {
  15. /*
  16. * 以下几个参数 具体作用和用法 详见 木蚂蚁接口文档
  17. */
  18. //分配的客户端 ID
  19. public $client_id;
  20. //分配的客户端 密匙
  21. public $client_secret;
  22. //获得的 用户授权 访问令牌
  23. public $access_token;
  24. //刷新 访问令牌 (过期时使用)
  25. public $refresh_token;
  26. // 最后一个收到的HTTP代码 (用于调试,忽略)
  27. public $http_code;
  28. //预留字段 (忽略)
  29. public $url;
  30. //设置 基础链接
  31. public $host = "http://server.ruanwenwu.cn/index.php/home/oauth/";
  32. //请求超时时间
  33. public $timeout = 30;
  34. //链接超时时间
  35. public $connecttimeout = 30;
  36. //是否验证 SSL 证书
  37. public $ssl_verifypeer = FALSE;
  38. //请求结果处理格式
  39. public $format = 'json';
  40. //以 JSON_DECODE 方式解析
  41. public $decode_json = TRUE;
  42. //预留字段 (忽略)
  43. public $http_info;
  44. //CURL 函数使用 (验证使用, 忽略)
  45. public $useragent = 'Mumayi Team OAuth2 v1.0';
  46. //是否 开启 请求调试 (开启时 将输出 详细的请求结果)
  47. public $debug = FALSE;
  48. //边界 (CURL使用 写入在Header的分界线, 忽略)
  49. public static $boundary = '';
  50. //返回 OAuth 令牌 请求地址
  51. function accessTokenURL() { return $this->host.'access_token'; }
  52. //返回 OAuth 自动授权 请求地址
  53. function authorizeURL() { return $this->host.'authorize'; }
  54. //构造函数 赋值 一些必要参数
  55. function __construct($client_id, $client_secret, $access_token = NULL, $refresh_token = NULL) {
  56. $this->client_id = $client_id;
  57. $this->client_secret = $client_secret;
  58. $this->access_token = $access_token;
  59. $this->refresh_token = $refresh_token;
  60. // $this->debug = true;
  61. }
  62. /**
  63. * authorize接口
  64. *
  65. * 对应API:{@link http://u.mumayi.com/oauth/authorize Oauth2/authorize}
  66. *
  67. * @param string $url 授权后的回调地址,站外应用需与回调地址一致,站内应用需要填写canvas page的地址
  68. * @param string $response_type 支持的值包括 code 和token 默认值为code
  69. * @param string $state 用于保持请求和回调的状态。在回调时,会在Query Parameter中回传该参数
  70. * @param string $display 授权页面类型 可选范围:
  71. * - default 默认授权页面
  72. * - mobile 支持html5的手机
  73. * - popup 弹窗授权页
  74. * - wap1.2 wap1.2页面
  75. * - wap2.0 wap2.0页面
  76. * - js js-sdk 专用 授权页面是弹窗,返回结果为js-sdk回掉函数
  77. * - apponweibo 站内应用专用,站内应用不传display参数,并且response_type为token时,默认使用改display.授权后不会返回access_token,只是输出js刷新站内应用父框架
  78. * @return array
  79. */
  80. function getAuthorizeURL( $url, $response_type = 'code', $state = NULL, $display = NULL ) {
  81. $params = array();
  82. $params['client_id'] = $this->client_id;
  83. $params['redirect_uri'] = $url;
  84. $params['response_type'] = $response_type;
  85. $params['state'] = $state;
  86. $params['display'] = $display;
  87. return $this->authorizeURL() . "&" . http_build_query($params);
  88. }
  89. /**
  90. * access_token接口
  91. *
  92. * 对应API:{@link http://open.weibo.com/wiki/OAuth2/access_token OAuth2/access_token}
  93. *
  94. * @param string $type 请求的类型,可以为:code, password, token
  95. * @param array $keys 其他参数:
  96. * - 当$type为code时: array('code'=>..., 'redirect_uri'=>...)
  97. * - 当$type为password时: array('username'=>..., 'password'=>...)
  98. * - 当$type为token时: array('refresh_token'=>...)
  99. * @return array
  100. */
  101. function getAccessToken( $type = 'code', $keys ) {
  102. $params = array();
  103. $params['client_id'] = $this->client_id;
  104. $params['client_secret'] = $this->client_secret;
  105. if ( $type === 'token' ) {
  106. $params['grant_type'] = 'refresh_token';
  107. $params['refresh_token'] = $keys['refresh_token'];
  108. } elseif ( $type === 'code' ) {
  109. $params['grant_type'] = 'authorization_code';
  110. $params['code'] = $keys['code'];
  111. $params['redirect_uri'] = $keys['redirect_uri'];
  112. } elseif ( $type === 'password' ) {
  113. $params['grant_type'] = 'password';
  114. $params['username'] = $keys['username'];
  115. $params['password'] = $keys['password'];
  116. } else {
  117. echo json_encode(array('error' => '错误的授权类型['.$type.'(token, code)]'));
  118. die;
  119. }
  120. $response = $this->oAuthRequest($this->accessTokenURL(), 'POST', $params);
  121. //var_dump($response);die;
  122. $token = json_decode($response, true);
  123. if ( is_array($token) && !isset($token['error']) ) {
  124. $this->access_token = $token['access_token'];
  125. //$this->refresh_token = $token['refresh_token'];
  126. } else {
  127. echo json_encode(array('error' => '获取访问令牌失败。['.$token['error'].']'));
  128. die;
  129. }
  130. return $token;
  131. }
  132. /**
  133. * 解析 signed_request
  134. *
  135. * @param string $signed_request 应用框架在加载iframe时会通过向Canvas URL post的参数signed_request
  136. *
  137. * @return array
  138. */
  139. function parseSignedRequest($signed_request) {
  140. list($encoded_sig, $payload) = explode('.', $signed_request, 2);
  141. $sig = self::base64decode($encoded_sig) ;
  142. $data = json_decode(self::base64decode($payload), true);
  143. if (strtoupper($data['algorithm']) !== 'HMAC-SHA256') return '-1';
  144. $expected_sig = hash_hmac('sha256', $payload, $this->client_secret, true);
  145. return ($sig !== $expected_sig)? '-2':$data;
  146. }
  147. /**
  148. * @ignore
  149. */
  150. function base64decode($str) {
  151. return base64_decode(strtr($str.str_repeat('=', (4 - strlen($str) % 4)), '-_', '+/'));
  152. }
  153. /**
  154. * 读取jssdk授权信息,用于和jssdk的同步登录
  155. *
  156. * @return array 成功返回array('access_token'=>'value', 'refresh_token'=>'value'); 失败返回false
  157. */
  158. function getTokenFromJSSDK() {
  159. $key = "mumayijs_" . $this->client_id;
  160. if ( isset($_COOKIE[$key]) && $cookie = $_COOKIE[$key] ) {
  161. parse_str($cookie, $token);
  162. if ( isset($token['access_token']) && isset($token['refresh_token']) ) {
  163. $this->access_token = $token['access_token'];
  164. $this->refresh_token = $token['refresh_token'];
  165. return $token;
  166. } else {
  167. return false;
  168. }
  169. } else {
  170. return false;
  171. }
  172. }
  173. /**
  174. * 从数组中读取access_token和refresh_token
  175. * 常用于从Session或Cookie中读取token,或通过Session/Cookie中是否存有token判断登录状态。
  176. *
  177. * @param array $arr 存有access_token和secret_token的数组
  178. * @return array 成功返回array('access_token'=>'value', 'refresh_token'=>'value'); 失败返回false
  179. */
  180. function getTokenFromArray( $arr ) {
  181. if (isset($arr['access_token']) && $arr['access_token']) {
  182. $token = array();
  183. $this->access_token = $token['access_token'] = $arr['access_token'];
  184. if (isset($arr['refresh_token']) && $arr['refresh_token']) {
  185. $this->refresh_token = $token['refresh_token'] = $arr['refresh_token'];
  186. }
  187. return $token;
  188. } else {
  189. return false;
  190. }
  191. }
  192. /**
  193. * 以 GET 请求方式 请求 OAuth 验证服务器
  194. *
  195. * @return mixed
  196. */
  197. function get($url, $parameters = array()) {
  198. $response = $this->oAuthRequest($url, 'GET', $parameters);
  199. if ($this->format === 'json' && $this->decode_json) {
  200. return json_decode($response, true);
  201. }
  202. return $response;
  203. }
  204. /**
  205. * 以 POST 请求方式 请求 OAuth 验证服务器
  206. *
  207. * @return mixed
  208. */
  209. function post($url, $parameters = array(), $multi = false) {
  210. $response = $this->oAuthRequest($url, 'POST', $parameters, $multi );
  211. if ($this->format === 'json' && $this->decode_json) {
  212. return json_decode($response, true);
  213. }
  214. return $response;
  215. }
  216. /**
  217. * 请求 OAuth 验证服务器 进行验证
  218. *
  219. * @return string
  220. */
  221. function oAuthRequest($url, $method, $parameters, $multi = false) {
  222. if (strrpos($url, 'http://') !== 0 && strrpos($url, 'https://') !== 0) {
  223. $url = "{$this->host}{$url}.{$this->format}";
  224. }
  225. switch ($method) {
  226. case 'GET':
  227. if(!empty($parameters))
  228. $url = $url . '&' . http_build_query($parameters);
  229. return $this->http($url, 'GET');
  230. default:
  231. $headers = array();
  232. if (!$multi && (is_array($parameters) || is_object($parameters)) ) {
  233. $body = http_build_query($parameters);
  234. } else {
  235. $body = self::build_http_query_multi($parameters);
  236. $headers[] = "Content-Type: multipart/form-data; boundary=" . self::$boundary;
  237. }
  238. //return $this->http($url, $method, $body, $headers);
  239. return $this->http($url, $method, $body, $headers);
  240. }
  241. }
  242. /**
  243. * 使用 CURL 模拟一个 HTTP 请求
  244. *
  245. * @description (需要开启 CURL 扩展)
  246. *
  247. * @return string API results
  248. *
  249. */
  250. function http($url, $method, $postfields = NULL, $headers = array()) {
  251. $this->http_info = array();
  252. $ci = curl_init();
  253. /* Curl settings */
  254. curl_setopt($ci, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
  255. curl_setopt($ci, CURLOPT_USERAGENT, $this->useragent);
  256. curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, $this->connecttimeout);
  257. curl_setopt($ci, CURLOPT_TIMEOUT, $this->timeout);
  258. curl_setopt($ci, CURLOPT_RETURNTRANSFER, TRUE);
  259. curl_setopt($ci, CURLOPT_ENCODING, "");
  260. curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, $this->ssl_verifypeer);
  261. curl_setopt($ci, CURLOPT_SSL_VERIFYHOST, 1);
  262. curl_setopt($ci, CURLOPT_HEADERFUNCTION, array($this, 'getHeader'));
  263. curl_setopt($ci, CURLOPT_HEADER, FALSE);
  264. switch ($method) {
  265. case 'POST':
  266. curl_setopt($ci, CURLOPT_POST, TRUE);
  267. if (!empty($postfields)) {
  268. curl_setopt($ci, CURLOPT_POSTFIELDS, $postfields);
  269. $this->postdata = $postfields;
  270. }
  271. break;
  272. case 'DELETE':
  273. curl_setopt($ci, CURLOPT_CUSTOMREQUEST, 'DELETE');
  274. if (!empty($postfields)) {
  275. $url = "{$url}?{$postfields}";
  276. }
  277. }
  278. if ( isset($this->access_token) && $this->access_token )
  279. $headers[] = "Authorization: OAuth2 ".$this->access_token;
  280. if ( !empty($this->remote_ip) ) {
  281. if ( defined('SAE_ACCESSKEY') ) {
  282. $headers[] = "SaeRemoteIP: " . $this->remote_ip;
  283. } else {
  284. $headers[] = "API-RemoteIP: " . $this->remote_ip;
  285. }
  286. } else {
  287. if ( !defined('SAE_ACCESSKEY') ) {
  288. $headers[] = "API-RemoteIP: " . $_SERVER['REMOTE_ADDR'];
  289. }
  290. }
  291. curl_setopt($ci, CURLOPT_URL, $url );
  292. curl_setopt($ci, CURLOPT_HTTPHEADER, $headers );
  293. curl_setopt($ci, CURLINFO_HEADER_OUT, TRUE );
  294. $response = curl_exec($ci);
  295. $this->http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE);
  296. $this->http_info = array_merge($this->http_info, curl_getinfo($ci));
  297. $this->url = $url;
  298. if ($this->debug) {
  299. echo "=====post data======\r\n";
  300. var_dump($postfields);
  301. echo "=====headers======\r\n";
  302. print_r($headers);
  303. echo '=====request info====='."\r\n";
  304. print_r( curl_getinfo($ci) );
  305. echo '=====response====='."\r\n";
  306. print_r( $response );
  307. }
  308. curl_close ($ci);
  309. return $response;
  310. }
  311. /**
  312. * 从结果中 获取 header 参数 (CURL 使用)
  313. *
  314. * @return int
  315. *
  316. * @ignore (忽略)
  317. */
  318. function getHeader($ch, $header) {
  319. $i = strpos($header, ':');
  320. if (!empty($i)) {
  321. $key = str_replace('-', '_', strtolower(substr($header, 0, $i)));
  322. $value = trim(substr($header, $i + 2));
  323. $this->http_header[$key] = $value;
  324. }
  325. return strlen($header);
  326. }
  327. /**
  328. * 把要传递的参数 重新构造一下 (多维数组)
  329. *
  330. * @ignore (忽略)
  331. */
  332. public static function build_http_query_multi($params) {
  333. if (!$params) return '';
  334. uksort($params, 'strcmp');
  335. $pairs = array();
  336. self::$boundary = $boundary = uniqid('------------------');
  337. $MPboundary = '--'.$boundary;
  338. $endMPboundary = $MPboundary. '--';
  339. $multipartbody = '';
  340. foreach ($params as $parameter => $value) {
  341. if( in_array($parameter, array('pic', 'image')) && $value{0} == '@' ) {
  342. $url = ltrim( $value, '@' );
  343. $content = file_get_contents( $url );
  344. $array = explode( '?', basename( $url ) );
  345. $filename = $array[0];
  346. $multipartbody .= $MPboundary . "\r\n";
  347. $multipartbody .= 'Content-Disposition: form-data; name="' . $parameter . '"; filename="' . $filename . '"'. "\r\n";
  348. $multipartbody .= "Content-Type: image/unknown\r\n\r\n";
  349. $multipartbody .= $content. "\r\n";
  350. } else {
  351. $multipartbody .= $MPboundary . "\r\n";
  352. $multipartbody .= 'content-disposition: form-data; name="' . $parameter . "\"\r\n\r\n";
  353. $multipartbody .= $value."\r\n";
  354. }
  355. }
  356. $multipartbody .= $endMPboundary;
  357. return $multipartbody;
  358. }
  359. public function get_uid() {
  360. $hostUrl = 'http://server.ruanwenwu.cn/index.php/home/resource/userinfo?type=mumayi';
  361. $params = array(
  362. 'access_token' => $this->access_token,
  363. );
  364. return $this->get( $hostUrl, $params );
  365. }
  366. }

注意:这个类的加载跟服务端采用的不同的方式。这里用了命名空间。服务端用的vendor。

3、准备访问链接。

我们通过这个url将用户导向server.ruanwenwu.cn进行授权,发送accesss_code操作。

我在client.ruanwenwu.cn的index控制器中实现的这个url。控制器里没有逻辑,我只贴index.html了:

  1. <!doctype html>
  2. <html>
  3. <head></head>
  4. <body>
  5. <a href="http://server.ruanwenwu.cn/index.php/home/oauth/authorize?redirect_uri=http://client.ruanwenwu.cn/index.php/home/login&response_type=code&client_id=10009174&type=123456">登陆</a>
  6. </body>
  7. </html>

注 意:这个url中redirect_uri,response_type为必选参数。而且这个redirect_uri必须包含我们在服务端添加 oauth服务时写的那个redirect_uri。否则不能通过验证。(也就是说:在我们这个例子里,这个redirect_uri必须包含:

  1. http://client.ruanwenwu.cn/index.php/home/login

4、点击链接,跳转到服务端对应的url。实际上就是server.ruanwenwu.cn的authorize操作。在这里会对用户进行验证,并判断是否授权。

  1. //权限验证
  2. public function authorize() {
  3. if (IS_POST) {
  4. if(true){
  5. //这个地方验证成功后要把用户的uid加进来
  6. $_POST['user_id'] = 1; //这里是写死的,实际上是从数据库读出来
  7. $this -> oauth -> finishClientAuthorization(true, $_POST); //第一个参数很重要,是用户是否授权
  8. }
  9. return;
  10. }
  11. ///表单准备
  12. $auth_params = $this -> oauth -> getAuthorizeParams();
  13. $this->assign("auth_params",$auth_params);
  14. $this->display();
  15. }

注意看我在这个操作里写的注释。

验证成功后,页面又跳回到我们之前写的redirect_uri,并带上了code参数,这就是验证服务器向客户端发送的access_code。

5、客户端拿到access_code,向验证服务器请求access_token。

在我的client.ruanwenwu.cn的loginController.class.php中进行:

  1. <?php
  2. namespace Home\Controller;
  3. use Think\Controller;
  4. use Org\Util\Oauth2;
  5. class LoginController extends Controller{
  6. public function _initialize(){
  7. }
  8. public function index(){
  9. $this->oauth = new Oauth2('10009174','123456');
  10. $keys = array();
  11. //keys 数组 赋值 code 值 (换取token, 必须参数)
  12. $keys['code'] = $_GET['code'];
  13. //keys 数组 赋值 回调地址信息 (换取token, 必须参数)
  14. $keys['redirect_uri'] = 'http://client.ruanwenwu.cn/index.php/home/login?type=mumayidev';
  15. //根据 code 获取 token
  16. //var_dump( $token = $this->oauth->getAccessToken( 'code', $keys )) ;
  17. $token = $this->oauth->getAccessToken( 'code', $keys ) ;
  18. //现在已经得到token,并且将access_token写到对象里了。就可以请求资源了
  19. var_dump( $this->oauth->get_uid());
  20. die;
  21. }
  22. }
  1. $token = $this->oauth->getAccessToken( 'code', $keys ) ; //这就是请求代码。code是验证方式。$keys数组里要code和redirect_uri。在服务器端需要验证

服务端的响应是这样的:

验证client.ruanwenwu.cn的client_id和secrete是否合法,还要判读access_code是否过期

  1. public function access_token() {
  2. $this -> oauth -> grantAccessToken(); //这个方法里有所有的验证及生成access_token的操作。(会有数据库的写入)
  3. }

服务端生成access_token之后,会返回给客户端。

客户端拿到access_token之后,向资源服务器请求用户资源:

在这里我的验证服务器和资源服务器都是server.ruanwenwu.cn。我用resourceController.class.php来做资源控制:

  1. <?php
  2. namespace Home\Controller;
  3. use Think\Controller;
  4. class ResourceController extends Controller{
  5. protected $db;
  6. public function _initialize(){
  7. $this->db = M();
  8. }
  9. public function userinfo(){
  10. // $arr = array("username"=>"阮文武","sex"=>1,"img"=>"http://weewrerfdf");
  11. // echo json_encode($arr);
  12. // die;
  13. if(isset($_GET['access_token']) && $_GET['access_token']){
  14. $res = $this->db->table("oauth_token")->where("access_token = '".$_GET['access_token']."'")->find();
  15. if($res && count($res) > 0){
  16. if($res['expires_in'] > time()){
  17. $arr = array("username"=>"阮文武d","sex"=>1,"img"=>"http://weewrerfdf");
  18. echo json_encode($arr);
  19. die;
  20. }
  21. }
  22. };
  23. }
  24. }

如果access_token有效,就查到用户id,取出用户的信息。我这里只是做测试,所以就直接自己返回了数据。原本写的很详细,后来因为内容字段是text,没保存完整,这会时间比较赶,就写的简单一点。

再抽时间补充吧。

欢迎大家与我讨论哦。

http://www.tuicool.com/articles/u6beUju

oauth2.0服务端与客户端搭建的更多相关文章

  1. Java的oauth2.0 服务端与客户端的实现

    oauth原理简述 oauth本身不是技术,而是一项资源授权协议,重点是协议!Apache基金会提供了针对Java的oauth封装.我们做Java web项目想要实现oauth协议进行资源授权访问,直 ...

  2. 创建自己的OAuth2.0服务端(一)

    如果对OAuth2.0有任何的疑问,请先熟悉OAuth2.0基础的文章:http://www.cnblogs.com/alunchen/p/6956016.html 1. 前言 本篇文章时对 客户端的 ...

  3. Oracle服务端及客户端搭建帮助文档

    Oracle服务端及客户端搭建帮助文档 目录 简介 Oracle服务端安装 Oracle客户端安装 PLSQL安装 登录测试 系统配置修改 用户操作 解锁账户.密码 创建账户及密码 配置监听文件 监听 ...

  4. Apache Oltu 实现 OAuth2.0 服务端【授权码模式(Authorization Code)】

    要实现OAuth服务端,就得先理解客户端的调用流程,服务提供商实现可能也有些区别,实现OAuth服务端的方式很多,具体可能看 http://oauth.net/code/ 各语言的实现有(我使用了Ap ...

  5. rsync的服务端和客户端搭建

    首先要看看有没有rsync,没有就按装一个rsync 1配置文件 然后创建rsyncd.conf文件,并添加如下内容(文件默认不存在) [root@chensiqi2 backup]# cat /et ...

  6. 写个OAuth2.0的请求端来测试自己的OAuth2.0服务端(二)

    在上一篇文章中,我们介绍了怎么创建自己的服务器,现在我们开始写个client端,来测试. 我们创建一个MVC项目,叫TestOAuthClient 1. 代码开始 1)第一步,我们创建一个MainCo ...

  7. SVN服务端和客户端的安装与搭建

    版权声明:本文为博主原创文章,转载请注明原文出处. https://blog.csdn.net/zzfenglin/article/details/50931462 SVN简介 SVN全名Subver ...

  8. Centos6.9 搭建rsync服务端与客户端 案例:全网备份项目

    rsync的企业工作场景说明 1)定时备份 1.1生产场景集群架构服务器备份方案项目 借助cron+rsync把所有客户服务器数据同步到备份服务器 2)实时复制 本地数据传输模式(local-only ...

  9. eureka服务端和客户端的简单搭建

    本篇博客简单记录一下,eureka 服务端和 客户端的简单搭建. 目标: 1.完成单机 eureka server 和 eureka client 的搭建. 2.完成eureka server 的添加 ...

随机推荐

  1. sql server中调用c#写的dll里的方法

    最近有一项目: 一超市管理系统单机版,运行在WIN2003+SQL2005上,每天超市关门都都会关电脑,现客户要新加功能,每天关门下班后回家可以上网查看超市管理系统的数据库里的相关数据,然后再做一些原 ...

  2. ios页面间跳转方式总结

    转自:http://www.cnblogs.com/anywherego/p/3542202.html 下面以OldViewController(oldC)的按钮btn点击后跳转到NewViewCon ...

  3. 通过反射实现圆角ImageView

    private void init(){ paint = new Paint(Paint.ANTI_ALIAS_FLAG); roundRect = , , getWidth() , getHeigh ...

  4. Django使用manage.py备份与恢复数据

    Django dumpdata and loaddata django database model dumpdata dumpdata command It is a django manageme ...

  5. zabbix server 与数据库不在同一台服务器上面

    16312:20170527:095215.225 database is down: reconnecting in 10 seconds 16312:20170527:095225.225 [Z3 ...

  6. Vmware虚拟硬盘合并多个分割文件

    有时,当我们创建虚拟机vmware里面的应用程序,我们可能会选择创建分割的虚拟磁盘中的多个文件2 GB的文件,这是为了提高复制过程,主要用于存储虚拟机文件系统不支持创建更大的文件. 如果我们需要将它转 ...

  7. 在javascript中获取一个对象内属性的个数

    var foo = {a1:'1',a2:'2',a3:'3'}; //获得对象所有属性的数组 Object.getOwnPropertyNames(foo); > [ 'a1', 'a2', ...

  8. elementui常用知识点总结

    1.淡入淡出效果: <transition name="el-fade-in-linear"> <div v-show="show" clas ...

  9. Ubuntu 系统下卸载 IntelliJ IDEA

    参考:http://blog.csdn.net/csdnones/article/details/50449947 卸载只需要删除解压出来的目录就行了,然后删除/home/你用登录名/IntelliJ ...

  10. python - hadoop,mapreduce demo

    Hadoop,mapreduce 介绍 59888745@qq.com 大数据工程师是在Linux系统下搭建Hadoop生态系统(cloudera是最大的输出者类似于Linux的红帽), 把用户的交易 ...