API接口设计 OAuth2.0认证

简介:

OAuth2.0官网

Java代码   收藏代码
  1. git clone https://github.com/bshaffer/oauth2-server-php.git  

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

Java代码   收藏代码
  1. CREATE TABLE `oauth_access_tokens` (  
  2. `access_token` varchar(40) NOT NULL,  
  3. `client_id` varchar(80) NOT NULL,  
  4. `user_id` varchar(255) DEFAULT NULL,  
  5. `expires` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,  
  6. `scope` varchar(2000) DEFAULT NULL,  
  7. PRIMARY KEY (`access_token`)  
  8. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  
  9.   
  10. CREATE TABLE `oauth_authorization_codes` (  
  11. `authorization_code` varchar(40) NOT NULL,  
  12. `client_id` varchar(80) NOT NULL,  
  13. `user_id` varchar(255) DEFAULT NULL,  
  14. `redirect_uri` varchar(2000) DEFAULT NULL,  
  15. `expires` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,  
  16. `scope` varchar(2000) DEFAULT NULL,  
  17. PRIMARY KEY (`authorization_code`)  
  18. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  
  19.   
  20. CREATE TABLE `oauth_clients` (  
  21. `client_id` varchar(80) NOT NULL,  
  22. `client_secret` varchar(80) NOT NULL,  
  23. `redirect_uri` varchar(2000) NOT NULL,  
  24. `grant_types` varchar(80) DEFAULT NULL,  
  25. `scope` varchar(100) DEFAULT NULL,  
  26. `user_id` varchar(80) DEFAULT NULL,  
  27. PRIMARY KEY (`client_id`)  
  28. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  
  29.   
  30. CREATE TABLE `oauth_refresh_tokens` (  
  31. `refresh_token` varchar(40) NOT NULL,  
  32. `client_id` varchar(80) NOT NULL,  
  33. `user_id` varchar(255) DEFAULT NULL,  
  34. `expires` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,  
  35. `scope` varchar(2000) DEFAULT NULL,  
  36. PRIMARY KEY (`refresh_token`)  
  37. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  
  38.   
  39. CREATE TABLE `oauth_users` (  
  40. `user_id` int(11) NOT NULL AUTO_INCREMENT,  
  41. `username` varchar(255) NOT NULL,  
  42. `password` varchar(2000) DEFAULT NULL,  
  43. `first_name` varchar(255) DEFAULT NULL,  
  44. `last_name` varchar(255) DEFAULT NULL,  
  45. PRIMARY KEY (`user_id`)  
  46. ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;  
  47.   
  48. CREATE TABLE `oauth_scopes` (  
  49.   `scope` text,  
  50.   `is_default` tinyint(1) DEFAULT NULL  
  51. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  
  52.   
  53. CREATE TABLE `oauth_jwt` (  
  54.   `client_id` varchar(80) NOT NULL,  
  55.   `subject` varchar(80) DEFAULT NULL,  
  56.   `public_key` varchar(2000) DEFAULT NULL,  
  57.   PRIMARY KEY (`client_id`)  
  58. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  
  59. -- test data  
  60. INSERT INTO oauth_clients (client_id, client_secret, redirect_uri) VALUES ("testclient""testpass""http://www.baidu.com/");  
  61. INSERT INTO oauth_users (username, password, first_name, last_name) VALUES ('rereadyou''8551be07bab21f3933e8177538d411e43b78dbcc''bo''zhang');  

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

配置

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

Java代码   收藏代码
  1. <?php  
  2. $dsn = 'mysql:dbname=test;host=localhost';  
  3. $username = 'root';  
  4. $password = 'orbit';  
  5.   
  6. // error reporting (this is a demo, after all!)  
  7. ini_set('display_errors'1);  
  8. error_reporting(E_ALL);  
  9.   
  10. // Autoloading (composer is preferred, but for this example let's just do this)  
  11. require_once('oauth2-server-php/src/OAuth2/Autoloader.php');  
  12. OAuth2\Autoloader::register();  
  13.   
  14. // $dsn is the Data Source Name for your database, for exmaple "mysql:dbname=my_oauth2_db;host=localhost"  
  15. $storage = new OAuth2\Storage\Pdo(array('dsn' => $dsn, 'username' => $username, 'password' => $password));  
  16.   
  17. // Pass a storage object or array of storage objects to the OAuth2 server class  
  18. //$server = new OAuth2\Server($storage);  
  19. $server = new OAuth2\Server($storage, array(  
  20.     'allow_implicit' => true,  
  21.     'refresh_token_lifetime'=> 2419200,  
  22. ));  
  23. // Add the "Client Credentials" grant type (it is the simplest of the grant types)  
  24. $server->addGrantType(new OAuth2\GrantType\ClientCredentials($storage));  
  25.   
  26. // Add the "Authorization Code" grant type (this is where the oauth magic happens)  
  27. $server->addGrantType(new OAuth2\GrantType\AuthorizationCode($storage));  
  28. //Resource Owner Password Credentials (资源所有者密码凭证许可)  
  29. $server->addGrantType(new OAuth2\GrantType\UserCredentials($storage));  
  30. //can RefreshToken set always_issue_new_refresh_token=true  
  31. $server->addGrantType(new OAuth2\GrantType\RefreshToken($storage, array(  
  32.     'always_issue_new_refresh_token' => true  
  33. )));  
  34. // configure your available scopes  
  35. $defaultScope = 'basic';  
  36. $supportedScopes = array(  
  37.     'basic',  
  38.     'postonwall',  
  39.     'accessphonenumber'  
  40. );  
  41. $memory = new OAuth2\Storage\Memory(array(  
  42.     'default_scope' => $defaultScope,  
  43.     'supported_scopes' => $supportedScopes  
  44. ));  
  45. $scopeUtil = new OAuth2\Scope($memory);  
  46. $server->setScopeUtil($scopeUtil);  

Token控制器

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

Java代码   收藏代码
  1. <?php  
  2. require_once __DIR__.'/server.php';  
  3.   
  4. // Handle a request for an OAuth2.0 Access Token and send the response to the client  
  5. $server->handleTokenRequest(OAuth2\Request::createFromGlobals())->send();  

测试Token控制器

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

Java代码   收藏代码
  1. curl -u testclient:testpass http://localhost/token.php -d 'grant_type=client_credentials'  

如果运行正常,则显示

Java代码   收藏代码
  1. {"access_token":"03807cb390319329bdf6c777d4dfae9c0d3b3c35","expires_in":3600,"token_type":"bearer","scope":null}  

资源控制器的建立和测试

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

Resource.php代码   收藏代码
  1. <?php  
  2. // include our OAuth2 Server object  
  3. require_once __DIR__ . '/server.php';  
  4.   
  5. // Handle a request for an OAuth2.0 Access Token and send the response to the client  
  6. if (!$server->verifyResourceRequest(OAuth2\Request::createFromGlobals())) {  
  7.     $server->getResponse()->send();  
  8.     die;  
  9. }  
  10. echo json_encode(array('success' => true, 'message' => 'You accessed my APIs!'));  

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

Java代码   收藏代码
  1. curl http://localhost/resource.php -d 'access_token=YOUR_TOKEN'  

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

Java代码   收藏代码
  1. {"success":true,"message":"You accessed my APIs!"}  

Authorization Code Grant (授权码认证)

Authorize.php代码   收藏代码
  1. <?php  
  2. // include our OAuth2 Server object  
  3. require_once __DIR__ . '/server.php';  
  4.   
  5. $request = OAuth2\Request::createFromGlobals();  
  6. $response = new OAuth2\Response();  
  7.   
  8. // validate the authorize request  
  9. if (!$server->validateAuthorizeRequest($request, $response)) {  
  10.     $response->send();  
  11.     die;  
  12. }  
  13. // display an authorization form  
  14. if (empty($_POST)) {  
  15.     exit('  
  16. <form method="post">  
  17.   <label>Do You Authorize TestClient?</label><br />  
  18.   <input type="submit" name="authorized" value="yes">  
  19. </form>');  
  20. }  
  21.   
  22. // print the authorization code if the user has authorized your client  
  23. $is_authorized = ($_POST['authorized'] === 'yes');  
  24. $server->handleAuthorizeRequest($request, $response, $is_authorized);  
  25. if ($is_authorized) {  
  26.     // this is only here so that you get to see your code in the cURL request. Otherwise, we'd redirect back to the client  
  27.     $code = substr($response->getHttpHeader('Location'), strpos($response->getHttpHeader('Location'), 'code=') + 540);  
  28.     //exit("SUCCESS AND DO redirect_uri! Authorization Code: $code");  
  29. }  
  30. $response->send();  
然后在浏览器中打开这个URL
Java代码   收藏代码
  1. http://localhost/authorize.php?response_type=code&client_id=testclient&state=xyz  
你将会看到一个表单,当你选择yes的时候会弹出你所获得的Authorization Code现在你可以用这个Authorization Code来刚才建立的token.php获得TOKEN,命令如下
Java代码   收藏代码
  1. curl -u testclient:testpass http://localhost/token.php -d 'grant_type=authorization_code&code=YOUR_CODE'  
就像刚才一样,你获得了一个TOKEN
Java代码   收藏代码
  1. {"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:首先,刷新令牌必须使用授权码或资源所有者密码凭证许可类型检索:

Java代码   收藏代码
  1. 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)
Java代码   收藏代码
  1. 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文件

Java代码   收藏代码
  1. $userid = 1// A value on your server that identifies the user  
  2. $server->handleAuthorizeRequest($request, $response, $is_authorized, $userid);  

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

Java代码   收藏代码
  1. if (!$server->verifyResourceRequest(OAuth2\Request::createFromGlobals())) {  
  2.     $server->getResponse()->send();  
  3.     die;  
  4. }   
  5. $token = $server->getAccessTokenData(OAuth2\Request::createFromGlobals());  
  6. echo "User ID associated with this token is {$token['user_id']}";  

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

Java代码   收藏代码
  1. curl -u testclient:testpass http://localhost/token.php -d 'grant_type=client_credentials&scope=postonwall'  

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

Java代码   收藏代码
  1. <?php  
  2. // include our OAuth2 Server object  
  3. require_once __DIR__ . '/server.php';  
  4.   
  5. $request = OAuth2\Request::createFromGlobals();  
  6. $response = new OAuth2\Response();  
  7. $scopeRequired = 'postonwall'// this resource requires "postonwall" scope  
  8. if (!$server->verifyResourceRequest($request, $response, $scopeRequired)) {  
  9.     // if the scope required is different from what the token allows, this will send a "401 insufficient_scope" error  
  10.     $server->getResponse()->send();  
  11.     die;  
  12. }  
  13. 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主要保证签名不可预测

Java代码   收藏代码
  1. $sign = new SignGenerator($params);  
  2. $sign->onSortAfter(function($that) use($key) {  
  3.     $that->key = $key;  
  4. });  
  5. $params['sign'] = $sign->getResult();  

 

相关文章
|
2月前
|
缓存 监控 测试技术
接口设计的18条军规:打造高效、可靠的API
【10月更文挑战第2天】在软件开发中,接口设计是连接不同模块、系统乃至服务的桥梁。一个优秀的接口设计不仅能提升开发效率,还能确保系统的稳定性和可扩展性。以下是接口设计的18条军规,旨在帮助你在工作和学习中设计出更加高效、可靠的API。
88 1
|
4月前
|
中间件 API 网络架构
Django后端架构开发:从匿名用户API节流到REST自定义认证
Django后端架构开发:从匿名用户API节流到REST自定义认证
46 0
|
4月前
|
API
【Azure Developer】调用Microsoft Graph API获取Authorization Token,使用的认证主体为 Azure中的Managed Identity(托管标识)
【Azure Developer】调用Microsoft Graph API获取Authorization Token,使用的认证主体为 Azure中的Managed Identity(托管标识)
|
4月前
|
JavaScript 前端开发 Linux
【Azure 应用服务】NodeJS Express + MSAL 实现API应用Token认证(AAD OAuth2 idToken)的认证实验 -- passport.authenticate()
【Azure 应用服务】NodeJS Express + MSAL 实现API应用Token认证(AAD OAuth2 idToken)的认证实验 -- passport.authenticate()
|
4月前
|
API 数据安全/隐私保护 网络架构
【Azure API 管理】解决调用REST API操作APIM(API Management)需要认证问题(Authentication failed, The 'Authorization' header is missing)
【Azure API 管理】解决调用REST API操作APIM(API Management)需要认证问题(Authentication failed, The 'Authorization' header is missing)
|
4月前
|
API
【Azure API 管理】在 Azure API 管理中使用 OAuth 2.0 授权和 Azure AD 保护 Web API 后端,在请求中携带Token访问后报401的错误
【Azure API 管理】在 Azure API 管理中使用 OAuth 2.0 授权和 Azure AD 保护 Web API 后端,在请求中携带Token访问后报401的错误
|
5月前
|
安全 API 网络安全
​邮箱OTP认证验证API发送邮件接口
**摘要 (Markdown格式):** OTP认证增强在线服务安全,尤其适用于邮箱验证。AOKSend提供邮箱OTP验证API,实现安全的邮件发送和用户身份验证。关键优势包括提高安全性、简化用户体验、实时发送、可扩展性和多层安全。配置涉及生成API密钥、设置SMTP、实现OTP逻辑、发送邮件及验证。AOKSend的分析工具帮助优化策略,适合各规模企业。
|
5月前
|
缓存 JSON 算法
http【详解】状态码,方法,接口设计 —— RestfuI API,头部 —— headers,缓存
http【详解】状态码,方法,接口设计 —— RestfuI API,头部 —— headers,缓存
77 0
|
存储 安全 API
互联网并发与安全系列教程(11) -OAuth2.0协议实现API设计
互联网并发与安全系列教程(11) -OAuth2.0协议实现API设计
84 0
|
7月前
|
JSON 安全 API
【专栏】四种REST API身份验证方法:基本认证、OAuth、JSON Web Token(JWT)和API密钥
【4月更文挑战第28天】本文探讨了四种REST API身份验证方法:基本认证、OAuth、JSON Web Token(JWT)和API密钥。基本认证简单但不安全;OAuth适用于授权第三方应用;JWT提供安全的身份验证信息传递;API密钥适合内部使用。选择方法时需平衡安全性、用户体验和开发复杂性。
850 0