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();  

 

相关文章
|
18天前
|
安全 API 数据安全/隐私保护
12种API认证全场景解析:从Basic到OAuth2.0,哪个认证最适合你的业务?
在API认证领域,从简单的Key-Value到高级的OAuth2.0和JWT,共有12种主流认证方式。本文详解了每种方式的意义、适用场景及优劣,并通过认证方式矩阵对比常见工具(如Postman、Apifox)的支持情况。此外,还介绍了企业级安全功能,如密钥保险箱、动态令牌和合规审计。选择合适的认证方式不仅能提升安全性,还能大幅提高开发效率。未来,自动化认证矩阵或将成为API调试的核心趋势。
|
1月前
|
安全 Java API
利用 AWS Signature:REST API 认证的安全指南
本文探讨了 AWS Signature 在保护 REST API 访问中的重要性,详解其工作原理,并提供 Java 和 Go 的实现示例。AWS Signature 通过加密技术确保请求安全,具备增强安全性、保障请求完整性、防范重放攻击及与 AWS 兼容等优势。文章还介绍了测试工具如 APIPost、Postman 和 cURL 的使用方法,帮助开发者验证实现效果。总结指出,采用 AWS Signature 可有效提升 API 安全性,增强用户信任并保护敏感数据。
|
1月前
|
安全 Java API
为什么要为 REST API 添加认证
在现代Web服务中,REST API的通信安全至关重要。认证机制可验证用户身份、控制资源访问、保护数据并监控使用情况。Basic Auth(基本认证)是一种简单有效的方法,通过HTTP头部发送Base64编码的用户名和密码实现安全保护,但建议搭配HTTPS使用以避免漏洞。本文展示了如何用Java和Go语言实现Basic Auth,并介绍了APIPost、Curl和Postman等工具进行测试。开发者可通过这些方法确保API功能强大且安全可靠。
|
2月前
|
存储 Cloud Native 安全
API 安全之认证鉴权
API 作为企业的重要数字资源,在给企业带来巨大便利的同时也带来了新的安全问题,一旦被攻击可能导致数据泄漏重大安全问题,从而给企业的业务发展带来极大的安全风险。
|
2月前
|
安全 API Go
如何实现和调试REST API中的摘要认证(Digest Authentication)
本文介绍如何实现和调试REST API中的摘要认证(Digest Authentication),涵盖其原理、优势及Java和Go语言的实现示例。摘要认证通过哈希算法处理密码,避免明文传输风险,并使用nonce防止重放攻击,确保数据完整性。文中还提供了Postman、cURL和Insomnia等工具的测试方法,帮助开发者轻松验证API的安全性。总结指出,摘要认证相比基本认证更安全,适合需要高安全性的API应用。
|
7月前
|
缓存 监控 测试技术
接口设计的18条军规:打造高效、可靠的API
【10月更文挑战第2天】在软件开发中,接口设计是连接不同模块、系统乃至服务的桥梁。一个优秀的接口设计不仅能提升开发效率,还能确保系统的稳定性和可扩展性。以下是接口设计的18条军规,旨在帮助你在工作和学习中设计出更加高效、可靠的API。
277 1
|
9月前
|
中间件 API 网络架构
Django后端架构开发:从匿名用户API节流到REST自定义认证
Django后端架构开发:从匿名用户API节流到REST自定义认证
144 0
|
9月前
|
API
【Azure Developer】调用Microsoft Graph API获取Authorization Token,使用的认证主体为 Azure中的Managed Identity(托管标识)
【Azure Developer】调用Microsoft Graph API获取Authorization Token,使用的认证主体为 Azure中的Managed Identity(托管标识)
|
9月前
|
JavaScript 前端开发 Linux
【Azure 应用服务】NodeJS Express + MSAL 实现API应用Token认证(AAD OAuth2 idToken)的认证实验 -- passport.authenticate()
【Azure 应用服务】NodeJS Express + MSAL 实现API应用Token认证(AAD OAuth2 idToken)的认证实验 -- passport.authenticate()
|
9月前
|
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)
168 0