zoukankan      html  css  js  c++  java
  • 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 协议的几个专业术语

    1. Third-party application: 第三方应用程序,又称“客户端”, 即上面例子中的“教程集”;
    2. HTTP service : HTTP服务提供商,即“服务提供商”, 即上面例子中的“微信”;
    3. Resource Owner: 资源所有者,即上面例子中的“用户”;
    4. User Agent:用户代理,在本文中即“浏览器”;
    5. Authorized server: 认证服务器,即服务提供商提供的专门用来做用户认证的服务器,在上面的例子中属于微信服务的一部分;
    6. Resource server: 资源服务器,即服务提供商提供的用户存放用户资源的服务器,在上面的例子中可以视为是一个查询用户好友关系的接口。

    Oauth2 运行的一般流程

    为了描述方便还是用微信举例子:

    1. 用打开客户端,客户端要求用户给予授权。
    2. 用户同意给予授权。
    3. 客户端使用上一步获取的授权码,向认证服务器换取令牌。
    4. 认证服务器检查客户端发来的授权码,确认无误,向客户端发放令牌。
    5. 客户端使用令牌,向资源服务器获取资源。
    6. 资源服务器验证令牌无误,同意向客户端开放资源。

    其中最关键的是第 “2”,即客户端如何获取用户的授权,客户端拿到授权码就可以向认证服务器换取令牌,Oauth2 提供了4种授权的方式:

    • 授权码模式 (authorized code)
    • 简化模式 (implict)
    • 密码模式 (resource owner password credential)
    • 客户端模式( client credentials)

    下面我们主要讲下常用的授权码模式。

    授权码模式

    授权码模式,即我们最常用的授权模式,目前微博、微信、QQ 等都是用的这种授权方式,这种授权模式是最严密,功能最完整的。

    主要流程如下:

    1. 用户访问客户端,客户端将用户导向(一般是 302跳转)认证服务器,
    2. 认证服务器判断用户是否已登录,如果没有登录,则在认证服务器提供的登录界面进行登录
    3. 假设已经登录,用户选择是否给予客户端授权(认证服务器一般会提供授权界面,用户可以选择开放哪些资源给客户端),假设用户给予授权,认证服务器将导向客户端实现指定的“重定向URI”(rediret_uri),同时在 redirect_uri 上附一个授权码
    4. 客户端收到授权码,附上早先的“重定向URL”, 向认证服务器申请令牌。(这一步在后台完成,用户不可见)
    5. 认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access_token)和更新令牌(refresh_token)。
    6. 客户端使用 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

    1.  
      <!doctype html>
    2.  
      <html lang="zh-CN">
    3.  
      <head>
    4.  
      <title>用户信息</title>
    5.  
      </head>
    6.  
      <body>
    7.  
       
    8.  
      <?php
    9.  
      require '../../vendor/autoload.php';
    10.  
      define('CLIENT_URL', 'http://oauth2-client.dev');
    11.  
      define('SERVER_URL', 'http://oauth2-server.dev');
    12.  
      define('REDIRECT_URI', CLIENT_URL.'/index.php');
    13.  
      define('RESOURCE_URL', SERVER_URL.'/resource.php');
    14.  
       
    15.  
      define('CLIENT_ID', 'testclient');
    16.  
      define('CLIENT_SECRET', 'testpass');
    17.  
       
    18.  
       
    19.  
      session_start();
    20.  
      function userInfo(){
    21.  
      if(isset($_SESSION['username'])) {
    22.  
      return $_SESSION;
    23.  
      } else {
    24.  
      return false;
    25.  
      }
    26.  
      }
    27.  
       
    28.  
       
    29.  
      if(isset($_REQUEST['logout'])) {
    30.  
      unset($_SESSION['username']);
    31.  
      session_destroy();
    32.  
      }
    33.  
       
    34.  
       
    35.  
      $userInfo = userInfo();
    36.  
      /*
    37.  
      * 接收用户中心返回的授权码
    38.  
      */
    39.  
      if (isset($_REQUEST['code']) && $_SERVER['REQUEST_URI']) {
    40.  
      //将认证服务器返回的授权码从 URL 中解析出来
    41.  
      $code = substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], 'code=')+5, 40);
    42.  
       
    43.  
      // 步骤4 拿授权码去申请令牌
    44.  
      $client = new GuzzleHttpClient();
    45.  
      $response = $client->request('POST', SERVER_URL.'/token.php', [
    46.  
      'auth' => [CLIENT_ID, CLIENT_SECRET],
    47.  
       
    48.  
      'form_params'=> [
    49.  
      'grant_type'=>'authorization_code',
    50.  
      'code'=> $code,
    51.  
      'redirect_uri'=> REDIRECT_URI,
    52.  
      ]
    53.  
      ]);
    54.  
       
    55.  
      $response = json_decode($response->getBody(), true);
    56.  
       
    57.  
      // 将令牌缓存到 SESSION中,方便后续访问
    58.  
      $_SESSION['access_token'] = $response['access_token'];
    59.  
       
    60.  
      // 步骤6 使用令牌获取用户信息
    61.  
      $response = $client->request('GET', RESOURCE_URL.'?access_token='.$_SESSION['access_token']);
    62.  
      $response = json_decode($response->getBody(), true);
    63.  
       
    64.  
      $userInfo = $response['userInfo'];
    65.  
      $_SESSION = array_merge($_SESSION, $userInfo);
    66.  
       
    67.  
      }
    68.  
       
    69.  
      // 步骤1,点击此链接跳转到认证中心
    70.  
      $auth_url = SERVER_URL."/authorize.php?response_type=code&client_id=testclient&state=xyz&redirect_uri=". REDIRECT_URI;
    71.  
       
    72.  
      ?>
    73.  
       
    74.  
      <?php if($userInfo): ?>
    75.  
      欢迎 <?php echo $userInfo['username'];?>, 头像 <img src="<?php echo $userInfo['avatar']; ?>" alt="" />
    76.  
      <a href="/index.php?logout=1">退出登录</a>
    77.  
      <?php else: ?>
    78.  
      <a href="<?php echo $auth_url ?>">使用媒体云登录</a>
    79.  
      <?php endif;?>
    80.  
      </body>
    81.  
      </html>
    82.  
       

    服务端:

    authorize.php

    1.  
      <?php
    2.  
      require_once __DIR__."/common.php";
    3.  
      $_SESSION['authorize_querystring'] = $_SERVER['QUERY_STRING'];
    4.  
       
    5.  
      // 步骤2 判断如果没有登录则跳转到登录界面
    6.  
      if(!isset($_SESSION['username']) && strpos($_SERVER['REQUEST_URI'], 'login.php') === false) {
    7.  
      header("Location: ".SERVER_URL.'/login.php');
    8.  
      exit;
    9.  
      }
    10.  
       
    11.  
      $request = OAuth2Request::createFromGlobals();
    12.  
       
    13.  
      $response = new OAuth2Response();
    14.  
       
    15.  
      if(!$server->validateAuthorizeRequest($request, $response)) {
    16.  
       
    17.  
      $response->send();
    18.  
      die;
    19.  
      }
    20.  
       
    21.  
       
    22.  
      if(empty($_POST)) {
    23.  
      // 步骤3 ,用户已经在认证中心登录,用户选择是否开放授权给客户端
    24.  
      exit('<form method="post">
    25.  
      <label>是否授权给 '.$_GET['client_id'].'?</label><br />
    26.  
      <input type="submit" name="authorized" value="yes">
    27.  
      <input type="submit" name="authorized" value="no">
    28.  
      </form>
    29.  
      <a href="/login.php?logout=1">退出登录</a>
    30.  
      ');
    31.  
      }
    32.  
       
    33.  
      // print the authorization code if the user has authorized your client
    34.  
      $is_authorized = ($_POST['authorized'] === 'yes');
    35.  
      $server->handleAuthorizeRequest($request, $response, $is_authorized);
    36.  
       
    37.  
      $response->send();
    38.  
       
    39.  
       

    token.php

      1.  
        <?php
      2.  
         
      3.  
        // 步骤5 ,认证服务器发放令牌
      4.  
        require_once __DIR__ . '/common.php';
      5.  
        $server->handleTokenRequest(OAuth2Request::createFromGlobals())->send();
      6.  
  • 相关阅读:
    Ellipse4SL 的安装包和环境配置要求
    什么是SOHO人
    D2上的Silverlight
    银光团队项目正式启动,欢迎您的参与!
    Silverlight开发团队招募SOHO团员若干
    SOHO新的生活方式
    [银客北京开发团队]承接各种Silverlight和WPF外包项目和合作项目
    微软Silverlight 3.0 概观
    grers
    翻译电文
  • 原文地址:https://www.cnblogs.com/lansetiankongblog/p/10559807.html
Copyright © 2011-2022 走看看