Oauth2.0 协议简介及 php实例代码
转自:http://www.dahouduan.com/2017/11/21/oauth2-php/
https://blog.csdn.net/halsonhe/article/details/81030319
Oauth2.0 是一个开源的授权协议,在全世界得到广泛应用,比较大的社交服务都支持了Oauth2.0 协议,例如 QQ,微博,微信。
Oauth2 协议的使用场景
假设有一个叫“教程集”的网站,可以通过读取用户在微信里的好友关系查询到还有谁也在学习教程,用户想使用该服务,就必须让“教程集”读取自己在微信里的好友关系。
微信只有得到用户的授权才会允许“教程集” 读取用户的好友关系,这时候传统的方式是,用户将微信帐号密码提交给“教程集”,“教程集”使用用户的帐号密码登录微信,再获取到用户的好友关系。
但是这样的做法有几个缺点:
- 为了后续用户不需要再次输入微信帐号密码,“教程集”不得不保存用户的微信帐号密码;
- 假设用户想收回对“教程集”的授权,只能修改密码,但是同时会使其他获得了用户授权的第三方程序全部失效;
- 用户无法控制“教程集”的权限范围和有效时间,用户将微信帐号密码都提交给“教程集”,“教程集”获得了几乎所有权限。
- 假设“教程集”被破解,所有用户的微信帐号密码同时泄露。
Oauth2 正是用来解决以上场景遇到的问题的。
Oauth2 协议的几个专业术语
- Third-party application: 第三方应用程序,又称“客户端”, 即上面例子中的“教程集”;
- HTTP service : HTTP服务提供商,即“服务提供商”, 即上面例子中的“微信”;
- Resource Owner: 资源所有者,即上面例子中的“用户”;
- User Agent:用户代理,在本文中即“浏览器”;
- Authorized server: 认证服务器,即服务提供商提供的专门用来做用户认证的服务器,在上面的例子中属于微信服务的一部分;
- Resource server: 资源服务器,即服务提供商提供的用户存放用户资源的服务器,在上面的例子中可以视为是一个查询用户好友关系的接口。
Oauth2 运行的一般流程
为了描述方便还是用微信举例子:
- 用打开客户端,客户端要求用户给予授权。
- 用户同意给予授权。
- 客户端使用上一步获取的授权码,向认证服务器换取令牌。
- 认证服务器检查客户端发来的授权码,确认无误,向客户端发放令牌。
- 客户端使用令牌,向资源服务器获取资源。
- 资源服务器验证令牌无误,同意向客户端开放资源。
其中最关键的是第 “2”,即客户端如何获取用户的授权,客户端拿到授权码就可以向认证服务器换取令牌,Oauth2 提供了4种授权的方式:
- 授权码模式 (authorized code)
- 简化模式 (implict)
- 密码模式 (resource owner password credential)
- 客户端模式( client credentials)
下面我们主要讲下常用的授权码模式。
授权码模式
授权码模式,即我们最常用的授权模式,目前微博、微信、QQ 等都是用的这种授权方式,这种授权模式是最严密,功能最完整的。
主要流程如下:
- 用户访问客户端,客户端将用户导向(一般是 302跳转)认证服务器,
- 认证服务器判断用户是否已登录,如果没有登录,则在认证服务器提供的登录界面进行登录
- 假设已经登录,用户选择是否给予客户端授权(认证服务器一般会提供授权界面,用户可以选择开放哪些资源给客户端),假设用户给予授权,认证服务器将导向客户端实现指定的“重定向URI”(rediret_uri),同时在 redirect_uri 上附一个授权码
- 客户端收到授权码,附上早先的“重定向URL”, 向认证服务器申请令牌。(这一步在后台完成,用户不可见)
- 认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access_token)和更新令牌(refresh_token)。
- 客户端使用
access_token向资源服务器获取资源。
步骤1中涉及的参数:
| 参数名 | 必填 | 说明 |
|---|---|---|
| response_type | 是 | 此处必须为“code” |
| client_id | 是 | 客户端id |
| redirect_uri | 否 | 重定向 URI |
| scope | 否 | 申请的授权范围 |
| state | 否 | 客户端当前状态,可以是任意值,认证服务器会原样返回这个参数 |
例子:
GET http://oauth2-server.dev/authorize.php?response_type=code&client_id=testclient&state=xyz&redirect_uri=http%3A%2F%2F127.0.0.1%3A8001%2F%2Fclient.php
步骤3 中,认证服务器回应客户端的URI,包含以下参数:
| 参数名 | 必填 | 说明 |
|---|---|---|
| code | 是 | 授权码, 该授权码有效期很短,例如30秒,且只能使用一次。 |
| state | 否 | 如果客户端中请求中包含这个参数,认证服务器的响应也必须包含同样的参数和值 |
例子
HTTP/1.1 302 Found
Location: http://oauth2-client.dev/client.php?code=ef2d9cd1bc71d99fa4ad193beab1bff48ec65df4&state=xyz
步骤4 中,客户端向认证服务器申请令牌,包含以下参数:
| 参数名 | 必填 | 说明 |
|---|---|---|
| grant_type | 是 | 表示授权模式,此处值固定为 ”authorized_code” |
| code | 是 | 上一步获得的授权码 |
| redirect_uri | 是 | 重定向URI,必须跟步骤1中的该参数值保持一致。 |
| client_id | 是 | 表示客户端id |
例子:
POST /token.php HTTP/1.1
Host: 127.0.0.1:8001
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA&&redirect_uri=http%3A%2F%2F127.0.0.1%3A8001%2F%2Fclient.php
步骤5 中,认证服务器回复 HTTP 请求,包含以下参数:
| 参数名 | 必填 | 说明 |
|---|---|---|
| access_token | 是 | 访问令牌 |
| token_type | 是 | 令牌类型,可以是 bearer 或者 mac 类型 |
| expire_in | 是 | 表示过期时间,单位为秒, |
| refresh_token | 否 | 用来获取下一次的令牌访问 |
| scope | 否 | 表示权限范围 |
例子:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA"
}
Cache-Control, 表示强制此接口不缓存。
PHP 实现Oauth2.0 授权码模式
为了方便的在我们自己的项目中集成 Oauth2, 可以使用一个第三方类库来实现 oauth2-server-php
下面通过一个Demo 来演示oauth2 的整个流程
搭建测试环境
代码分为客户端、服务端两个部分:
克隆测试代码到本地
git clone git@github.com:shanhuhai/oauth2-demo.git
cd oauth2-demo
composer install
克隆完代码后, 在 oauth2-demo/example 下面有两个目录分别为 client 和 server 对应客户端和服务端。
将这两个目录在nginx分别绑定 oauth2-client.dev 和 oauth2-server.dev 域名。
安装数据库
创建一个数据库 ‘my_oauth2_db’, 下载这个sql 文件并导入数据库中
Oauth2演示用数据表
打开 oauth2-demo/example/server/common.php 配置好数据库信息。
开始测试
在你的hosts 文件中,配置
127.0.0.1 oauth2-client.dev
127.0.0.1 oauth2-server.dev
在浏览器打开 http://oauth2-client.dev/index.php,就可以测试了,
默认提供的测试帐号是 “shanhuhai”, 密码 “123123”
代码说明
在代码注释中标明了业务流程的关键点,对应前文列出的主要步骤。
客户端:
客户端只有一个文件。
index.php
<!doctype html><html lang="zh-CN"><head><title>用户信息</title></head><body><?phprequire '../../vendor/autoload.php';define('CLIENT_URL', 'http://oauth2-client.dev');define('SERVER_URL', 'http://oauth2-server.dev');define('REDIRECT_URI', CLIENT_URL.'/index.php');define('RESOURCE_URL', SERVER_URL.'/resource.php');define('CLIENT_ID', 'testclient');define('CLIENT_SECRET', 'testpass');session_start();function userInfo(){if(isset($_SESSION['username'])) {return $_SESSION;} else {return false;}}if(isset($_REQUEST['logout'])) {unset($_SESSION['username']);session_destroy();}$userInfo = userInfo();/** 接收用户中心返回的授权码*/if (isset($_REQUEST['code']) && $_SERVER['REQUEST_URI']) {//将认证服务器返回的授权码从 URL 中解析出来$code = substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], 'code=')+5, 40);// 步骤4 拿授权码去申请令牌$client = new GuzzleHttp\Client();$response = $client->request('POST', SERVER_URL.'/token.php', ['auth' => [CLIENT_ID, CLIENT_SECRET],'form_params'=> ['grant_type'=>'authorization_code','code'=> $code,'redirect_uri'=> REDIRECT_URI,]]);$response = json_decode($response->getBody(), true);// 将令牌缓存到 SESSION中,方便后续访问$_SESSION['access_token'] = $response['access_token'];// 步骤6 使用令牌获取用户信息$response = $client->request('GET', RESOURCE_URL.'?access_token='.$_SESSION['access_token']);$response = json_decode($response->getBody(), true);$userInfo = $response['userInfo'];$_SESSION = array_merge($_SESSION, $userInfo);}// 步骤1,点击此链接跳转到认证中心$auth_url = SERVER_URL."/authorize.php?response_type=code&client_id=testclient&state=xyz&redirect_uri=". REDIRECT_URI;?><?php if($userInfo): ?>欢迎 <?php echo $userInfo['username'];?>, 头像 <img src="<?php echo $userInfo['avatar']; ?>" alt="" /><a href="/index.php?logout=1">退出登录</a><?php else: ?><a href="<?php echo $auth_url ?>">使用媒体云登录</a><?php endif;?></body></html>
服务端:
authorize.php
<?phprequire_once __DIR__."/common.php";$_SESSION['authorize_querystring'] = $_SERVER['QUERY_STRING'];// 步骤2 判断如果没有登录则跳转到登录界面if(!isset($_SESSION['username']) && strpos($_SERVER['REQUEST_URI'], 'login.php') === false) {header("Location: ".SERVER_URL.'/login.php');exit;}$request = OAuth2\Request::createFromGlobals();$response = new \OAuth2\Response();if(!$server->validateAuthorizeRequest($request, $response)) {$response->send();die;}if(empty($_POST)) {// 步骤3 ,用户已经在认证中心登录,用户选择是否开放授权给客户端exit('<form method="post"><label>是否授权给 '.$_GET['client_id'].'?</label><br /><input type="submit" name="authorized" value="yes"><input type="submit" name="authorized" value="no"></form><a href="/login.php?logout=1">退出登录</a>');}// print the authorization code if the user has authorized your client$is_authorized = ($_POST['authorized'] === 'yes');$server->handleAuthorizeRequest($request, $response, $is_authorized);$response->send();
token.php
<?php// 步骤5 ,认证服务器发放令牌require_once __DIR__ . '/common.php';$server->handleTokenRequest(OAuth2\Request::createFromGlobals())->send();
Oauth2.0 协议简介及 php实例代码的更多相关文章
- 第三方登录,一般都是遵循OAuth2.0协议。
1. QQ登录OAuth2.0协议开发流程 1.1 开发流程 申请接入,获取appid和appkey; 开发应用,设置协作者账号,上线之前只有协作者才能进行第三方登录 放置QQ登录按钮(这个自己可以用 ...
- 使用OAuth2.0协议的github、QQ、weibo第三方登录接入总结
目录 第三方接入总结 OAuth2.0介绍 github OAuth2.0登录接入 国内第三方应用商SDK使用 微博SDK 腾讯QQ SDK passport.js插件使用 安装 相关中间件.路由 返 ...
- Oauth2.0协议曝漏洞 大量社交网站隐私或遭泄露
2014年是IT业界不平常的一年,XP停服.IE长老漏洞(秘狐)等等层出不穷,现在,社交网络也爆出惊天漏洞:Oauth2.0协议漏洞 继OpenSSL漏洞后,开源安全软件再曝安全漏洞.新加坡南洋理工大 ...
- 《OAuth2.0协议安全形式化分析-》----论文摘抄整理
---恢复内容开始--- 本篇论文发表在计算机工程与设计,感觉写的还是很有水准的.实验部分交代的比较清楚 本篇论文的创新点: 使用Scyther工具 主要是在 DY模型下面 形式化分析了 OAuth2 ...
- Spring Security 实战干货: 简单的认识 OAuth2.0 协议
1.前言 欢迎阅读 Spring Security 实战干货 系列文章 .OAuth2.0 是近几年比较流行的授权机制,对于普通用户来说可能每天你都在用它,我们经常使用的第三方登录大都基于 OAuth ...
- 对OAuth2.0协议的理解和测试demo
1. 什么是OAuth OAuth(开放授权)是一个开放标准,允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他们数据的所有内容. OAuth ...
- 接口测试工具-Jmeter使用笔记(八:模拟OAuth2.0协议简化模式的请求)
背景 博主的主要工作是测试API,目前已经用Jmeter+Jenkins实现了项目中的接口自动化测试流程.但是马上要接手的项目,API应用的是OAuth2.0协议授权,并且采用的是简化模式(impli ...
- 轻松搭建CAS 5.x系列(6)-在CAS Server上增加OAuth2.0协议
概述说明 CAS Server默认搭建出来,客户端程序只能按照CAS自身的协议接入.CAS的强大在于,有官方的插件,可以支持其他的协议.本章节就让CAS Server怎么增加OAuth2.0的登录协议 ...
- 理解OAuth2.0协议和授权机制
无论是自然资源还是互联网上的资源,需要控制使用权与被使用权,以保护资源的安全.合理的使用和有效的管控. 项目中,我们需要控制的是用户资源,既要保证有效用户的合理使用,又要防范非法用户的攻击.如此,如何 ...
随机推荐
- 基于libcurl的GET与POST(HTTP1.1)
#include <stdio.h> #include <curl/curl.h> bool getUrl(char *filename) { CURL *curl; CURL ...
- Activity组件:(一)通过显式意图和隐式意图来实现Activity间的跳转
一.通过显式意图来实现Activity间的跳转 显式意图是指在创建Intent对象时就指定接受者组件 /** * 下面是通过显式意图进行跳转,即明确写出要跳转到SecondActivity.class ...
- UVA 10269 Super Mario,最短路+动态规划
这个题目我昨晚看到的,没什么思路,因为马里奥有boot加速器,只要中间没有城堡,即可不耗时间和脚力,瞬间移动不超过L距离,遇见城堡就要停下来,当然不能该使用超过K次...我纠结了很久,最终觉得还是只能 ...
- webview Java与JS互调
Android调用JS:方法一 webView.loadUrl("javascript:show('"+info+"')"); Android调用JS:方法二 ...
- HTML字符实体和转义字符串大全
转义字符串的组成 转义字符串(Escape Sequence),即字符实体(Character Entity)分成三部分:第一部分是一个&符号,英文叫ampersand:第二部分是实体(Ent ...
- 利用GetCharWidth32获取字符串宽度2
/////////////////////////////////////////////////////////////// // 04FirstWindow.cpp文件 #include < ...
- python图像处理常用方法
在线标注网站 https://gitlab.com/vgg/via http://www.robots.ox.ac.uk/~vgg/software/via/via.html 数组与图像互转 from ...
- Ubuntu16装Flash
第一种方法: 1)下载flash的tar.gz压缩包.(以·tar.gz为后缀的文件是一种压缩文件,在Linux和macOS下常见,Linux和macOS都可以直接解压使用这种压缩文件) https: ...
- Java常见异常说明汇总
1. java.lang.nullpointerexception 这个异常大家肯定都经常遇到,异常的解释是"程序遇上了空指针",简单地说就是调用了未经初始化的对象或者是不存在的对 ...
- 大数据学习——MapReduce学习——字符统计WordCount
操作背景 jdk的版本为1.8以上 ubuntu12 hadoop2.5伪分布 安装 Hadoop-Eclipse-Plugin 要在 Eclipse 上编译和运行 MapReduce 程序,需要安装 ...