在PHP中使用Auth0进行身份验证和授权

本文介绍了如何在PHP Web应用中使用Auth0进行身份验证和授权。通过Auth0服务,可以轻松实现单点登录、多因素认证等功能。文章详细讲解了服务器端身份验证的集成,包括设置项目、登录和注销脚本,以及如何使用OAuth2保护自定义API,包括获取访问令牌、调用API端点和验证令牌的过程。
摘要由CSDN通过智能技术生成

在本文中,我们将探索Auth0服务,该服务提供身份验证和授权即服务。 Auth0允许您在瞬间设置应用程序的基本身份验证和授权功能。

什么是Auth0?

Auth0是一种身份验证即服务工具,可轻松实现站点中与身份验证相关的功能的实现。 如果您已经构建了一个应用程序并且只想将身份验证和授权功能外包,则应考虑使用Auth0之类的服务。

让我快速总结一下Auth0提供的功能:

  • 单点登录
  • 多因素认证
  • 无密码登录
  • 用户管理
  • 以及更多

在本文中,我们将介绍一些可以在Web应用程序中实现的单点登录方法,以便利用Auth0服务提供的身份验证功能。

在本文的前半部分,我们将探讨如何在服务器端PHP Web应用程序中设置基本身份验证功能。 在下半部分,我将解释如何通过使用Auth0服务设置OAuth授权来保护自定义API。

服务器端身份验证集成

在本节中,我们将介绍如何使用Auth0快速设置服务器端Web应用程序的基本身份验证。 实际上,Auth0团队已经提供了一个方便的GitHub示例,用于演示基本示例,因此我们将使用它而不是重新发明轮子。

在继续之前,请确保安装Composer,因为它将使用composer.json文件安装实际的Auth0 SDK。 另外,如果您想按照本文中的示例进行操作,请继续使用Auth0免费帐户。

设置项目

让我们继续进行示例项目的克隆。

git clone https://github.com/auth0-samples/auth0-php-web-app.git .

继续运行composer install命令以安装依赖项。

cd 00-Starter-Seed
composer install

根据composer.json文件,它应该已经安装了vlucas/phpdotenvauth0/auth0-php软件包。

{
    "name": "auth0/basic-webapp-sample",
    "description": "Basic sample for securing a WebApp with Auth0",
    "require": {
         "vlucas/phpdotenv": "2.4.0",
         "auth0/auth0-php": "~5.0"
    },
    "license": "MIT",
    "authors": [
        {
            "name": "Martin Gontovnikas",
            "email": "martin@gon.to"
        },
        {
            "name": "Germán Lena",
            "email": "german.lena@gmail.com"
        }
    ]
}

vlucas/phpdotenv库用于初始化.env文件中的环境变量。 因此,它允许您将配置与在环境之间更改的代码分开。

另一方面, auth0/auth0-php软件包将帮助我们在应用程序中设置授权。

接下来,让我们在.env文件中设置应用程序的配置。 继续并通过从.env.example文件中复制该文件来创建.env文件。

cp .env.example .env

它包含将由Auth0库使用的配置值。

AUTH0_CLIENT_ID={CLIENT_ID}
AUTH0_DOMAIN={DOMAIN_NAME}
AUTH0_CLIENT_SECRET={CLIENT_SECRET}
AUTH0_CALLBACK_URL={CALLBACK_URL}
AUTH0_AUDIENCE=

您应该能够在Auth0信息中心的“应用程序”>“默认应用程序”>“设置”下找到大多数设置。 请注意,我使用的是系统创建的默认应用程序。 当然,如果需要,您可以继续创建一个新的应用程序。

AUTH0_CALLBACK_URL是您的应用程序的URL,Auth0将在登录和注销后重定向用户。 您必须在Auth0仪表板上的应用程序设置下的“ Allowed Callback URLs下配置在此字段中设置的值。

您将找到实现大多数身份验证逻辑的三个主要文件。

  • index.php :这是基于用户状态显示登录或注销按钮的主页。
  • login.php :当您单击登录按钮时,将启动此脚本,它将把用户重定向到Auth0登录界面进行登录。 登录后,它们将被重定向回AUTH0_CALLBACK_URL
  • logout.php :当您单击注销按钮时,将启动此脚本,它将在后台将用户重定向到Auth0,注销用户,然后将其返回到AUTH0_CALLBACK_URL

重点项目文件

让我们快速浏览入门项目中的每个文件。

登录脚本

我们将从login.php文件开始。

<?php
require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/dotenv-loader.php';

use Auth0\SDK\Auth0;

$domain        = getenv('AUTH0_DOMAIN');
$client_id     = getenv('AUTH0_CLIENT_ID');
$client_secret = getenv('AUTH0_CLIENT_SECRET');
$redirect_uri  = getenv('AUTH0_CALLBACK_URL');
$audience      = getenv('AUTH0_AUDIENCE');

if($audience == ''){
$audience = 'https://' . $domain . '/userinfo';
}

$auth0 = new Auth0([
  'domain' => $domain,
  'client_id' => $client_id,
  'client_secret' => $client_secret,
  'redirect_uri' => $redirect_uri,
  'audience' => $audience,
  'scope' => 'openid profile',
  'persist_id_token' => true,
  'persist_access_token' => true,
  'persist_refresh_token' => true,
]);

$auth0->login();

首先,我们包括了自动加载器,它们负责加载Auth0和环境变量相关的类。 然后,我们使用getenv函数从.env文件初始化配置变量。

接下来,我们实例化Auth0对象并调用login方法,该方法将用户重定向到Auth0进行登录。 登录后,用户将被重定向回我们的网站。

您可以使用Facebook,Google等社交帐户登录,也可以在登录期间创建新帐户。 无论哪种情况,Auth0都会为新用户创建记录。 您可以在Auth0仪表板上的“ 连接”>“社交”下启用其他社交登录。 另外,您可以在“ 用户”链接下的Auth0仪表板上检查使用Auth0登录的用户列表。

注销脚本

接下来,让我们快速查看logout.php文件。

<?php
require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/dotenv-loader.php';
use Auth0\SDK\Auth0;

$domain        = getenv('AUTH0_DOMAIN');
$client_id     = getenv('AUTH0_CLIENT_ID');
$client_secret = getenv('AUTH0_CLIENT_SECRET');
$redirect_uri  = getenv('AUTH0_CALLBACK_URL');
$audience      = getenv('AUTH0_AUDIENCE');

if($audience == ''){
    $audience = 'https://' . $domain . '/userinfo';
}

$auth0 = new Auth0([
  'domain' => $domain,
  'client_id' => $client_id,
  'client_secret' => $client_secret,
  'redirect_uri' => $redirect_uri,
  'audience' => $audience,
  'scope' => 'openid profile',
  'persist_id_token' => true,
  'persist_refresh_token' => true,
]);

$auth0->logout();
$return_to = 'https://' . $_SERVER['HTTP_HOST'];
$logout_url = sprintf('http://%s/v2/logout?client_id=%s&returnTo=%s', $domain, $client_id, $return_to);
header('Location: ' . $logout_url);
die();

它的工作原理与login.php文件几乎相同,除了在用户注销时将调用它。 调用logout方法可使应用程序中的用户会话期满。 之后,用户将被重定向到Auth0,以便向服务通知用户的注销活动。 最后,用户将被重定向回您的应用程序。

索引文件

最后,让我们浏览index.php文件,这是我们应用程序的入口。

<?php
  // Require composer autoloader
  require __DIR__ . '/vendor/autoload.php';
  require __DIR__ . '/dotenv-loader.php';
  use Auth0\SDK\Auth0;
  $domain        = getenv('AUTH0_DOMAIN');
  $client_id     = getenv('AUTH0_CLIENT_ID');
  $client_secret = getenv('AUTH0_CLIENT_SECRET');
  $redirect_uri  = getenv('AUTH0_CALLBACK_URL');
  $audience      = getenv('AUTH0_AUDIENCE');
  if($audience == ''){
    $audience = 'https://' . $domain . '/userinfo';
  }
  $auth0 = new Auth0([
    'domain' => $domain,
    'client_id' => $client_id,
    'client_secret' => $client_secret,
    'redirect_uri' => $redirect_uri,
    'audience' => $audience,
    'scope' => 'openid profile',
    'persist_id_token' => true,
    'persist_access_token' => true,
    'persist_refresh_token' => true,
  ]);
  $userInfo = $auth0->getUser();
?>
<html>
    <head>
        <script src="http://code.jquery.com/jquery-3.1.0.min.js" type="text/javascript"></script>

        <meta name="viewport" content="width=device-width, initial-scale=1">

        <!-- font awesome from BootstrapCDN -->
        <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
        <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css" rel="stylesheet">

        <link href="public/app.css" rel="stylesheet">
    </head>
    <body class="home">
        <div class="container">
            <div class="login-page clearfix">
              <?php if(!$userInfo): ?>
              <div class="login-box auth0-box before">
                <img src="https://i.cloudup.com/StzWWrY34s.png" />
                <h3>Auth0 Example</h3>
                <p>Zero friction identity infrastructure, built for developers</p>
                <a class="btn btn-primary btn-lg btn-login btn-block" href="login.php">Sign In</a>
              </div>
              <?php else: ?>
              <div class="logged-in-box auth0-box logged-in">
                <h1 id="logo"><img src="//cdn.auth0.com/samples/auth0_logo_final_blue_RGB.png" /></h1>
                <img class="avatar" src="<?php echo $userInfo['picture'] ?>"/>
                <h2>Welcome <span class="nickname"><?php echo $userInfo['nickname'] ?></span></h2>
                <a class="btn btn-warning btn-logout" href="/logout.php">Logout</a>
              </div>
              <?php endif ?>
            </div>
        </div>
    </body>
</html>

在这里,我们使用了$auth0对象的getUser方法来查看是否存在任何活动会话。 如果没有活动的会话,我们将显示“ 登录”链接,该链接会将用户带到login.php并启动登录流程。 另一方面,如果用户已经登录,我们将向该用户打招呼并显示“ 注销”链接。

这样就实现了服务器端应用程序的基本身份验证流程。

使用OAuth2保护您的自定义API

相反,我们将直接深入实际的实现。 继续并创建具有以下内容的auth_code_grant_example.php文件。

<?php
session_start();

if (!isset($_GET['code'])) {
    // Check if we need to show the "Sign In" link
    $params = array (
      'audience' => '{AUDIENCE}',
      'scope' => 'profile',
      'response_type' => 'code',
      'client_id' => '{CLIENT_ID}',
      'state' => 'SomeRandomString',
      'redirect_uri' => '{CALLBACK_URL}'
    );

    $_SESSION['oauth2state']=$params['state'];
    $str_params = '';
    foreach($params as $key=>$value) {
      $str_params .= $key . "=" . urlencode($value) . "&";
    }
    ?>

    <a href="https://{AUTH0_DOMAIN}/authorize?<?php echo $str_params;?>">
      Sign In
    </a>
<?php
} elseif (empty($_GET['state']) || (isset($_SESSION['oauth2state']) && $_GET['state'] !== $_SESSION['oauth2state'])) {
    // If the "state" var is present in the $_GET, let's validate it
    if (isset($_SESSION['oauth2state'])) {
        unset($_SESSION['oauth2state']);
    }
    
    exit('Invalid state');

} elseif(isset($_GET['code']) && !empty($_GET['code'])) {
    // If the auth "code" is present in the $_GET
    // let's exchange it for the access token
    $params = array (
      'grant_type' => 'authorization_code',
      'client_id' => '{CLIENT_ID}',
      'client_secret' => '{CLIENT_SECRET}',
      'code' => $_GET['code'],
      'redirect_uri' => '{CALLBACK_URL}'
    );

    $str_params = '';
    foreach($params as $key=>$value) {
      $str_params .= $key . "=" . urlencode($value) . "&";
    }

    $curl = curl_init();

    curl_setopt_array($curl, array(
      CURLOPT_URL => "https://{AUTH0_DOMAIN}/oauth/token",
      CURLOPT_RETURNTRANSFER => true,
      CURLOPT_CUSTOMREQUEST => "POST",
      CURLOPT_POSTFIELDS => $str_params
    ));

    $curl_response = curl_exec($curl);
    $curl_error = curl_error($curl);

    curl_close($curl);

    if ($curl_error) {
      echo "Error in the CURL response:" . $curl_error;
    } else {
      $arr_json_data = json_decode($curl_response);

      if (isset($arr_json_data->access_token)) {
        $access_token = $arr_json_data->access_token;
        $curl = curl_init();

        curl_setopt_array($curl, array(
          CURLOPT_URL => "http://{YOUR_API_DOMAIN}/demo_api_server.php",
          CURLOPT_RETURNTRANSFER => true,
          CURLOPT_CUSTOMREQUEST => "GET",
          CURLOPT_HTTPHEADER => array(
            "Authorization: Bearer {$access_token}"
          )
        ));

        $curl_response = curl_exec($curl);
        $curl_error = curl_error($curl);

        curl_close($curl);

        if ($curl_error) {
          echo "Error in the CURL response from DEMO API:" . $curl_error;
        } else {
          echo "Demo API Response:" . $curl_response;
        }
      } else {
        echo 'Invalid response, no access token was found.';
      }
    }
}

让我们看看这段代码是如何工作的!

开始授权流程

首先,我们准备了一个链接,该链接将用户发送到Auth0服务器以开始授权流程。

// Check if we need to show the "Sign In" link
$params = array (
  'audience' => '{AUDIENCE}',
  'scope' => 'profile',
  'response_type' => 'code',
  'client_id' => '{CLIENT_ID}',
  'state' => '{SOME_RANDOM_STRING}',
  'redirect_uri' => '{CALLBACK_URL}'
);

$_SESSION['oauth2state']=$params['state'];
$str_params = '';
foreach($params as $key=>$value) {
  $str_params .= $key . "=" . urlencode($value) . "&";
}
?>

<a href="https://{AUTH0_DOMAIN}/authorize?<?php echo $str_params;?>">
  Sign In
</a>

请用与您的应用程序对应的值替换{AUDIENCE}{CLIENT_ID}{CALLBACK_URL}{AUDIENCE}参数应替换为Auth0仪表板上的API> {您的API应用程序}>设置下的标识符字段的值。

{SOME_RANDOM_STRING}应该替换为难以猜测的唯一值。 该字符串用于防止CSRF攻击。 另外,请确保将{AUTH0_DOMAIN}替换为您的域名,如我们之前所述。

获取访问令牌

当用户单击“ 登录”链接时,他们将被带到Auth0服务器进行身份验证。 身份验证后,将要求他们授权应用程序访问您的个人资料。 授权后,用户将使用code作为$_GET参数重定向回到您的应用程序。

接下来,我们可以交换此code以获取访问令牌。

// If the auth "code" is present in the $_GET
// let's exchange it for the access token
$params = array (
  'grant_type' => 'authorization_code',
  'client_id' => '{CLIENT_ID}',
  'client_secret' => '{CLIENT_SECRET}',
  'code' => $_GET['code'],
  'redirect_uri' => '{CALLBACK_URL}'
);

$str_params = '';
foreach($params as $key=>$value) {
  $str_params .= $key . "=" . urlencode($value) . "&";
}

$curl = curl_init();
$curl_response = curl_exec($curl);

curl_setopt_array($curl, array(
  CURLOPT_URL => "https://{AUTH0_DOMAIN}/oauth/token",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => $str_params
));

如您所见,只需要一个CURL调用即可获取访问令牌。

调用您的自定义API端点

获得访问令牌后,可以通过将其包含在标头中来调用自定义API端点。

$access_token = $arr_json_data->access_token;
$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "http://{YOUR_API_DOMAIN}/demo_api_server.php",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_HTTPHEADER => array(
    "Authorization: Bearer {$access_token}"
  )
));

$curl_response = curl_exec($curl);
$curl_error = curl_error($curl);

curl_close($curl);

if ($curl_error) {
  echo "Error in the CURL response from DEMO API:" . $curl_error;
} else {
  echo "Demo API Response:" . $curl_response;
}

受Auth0保护的API端点

虚拟API资源文件demo_api_server.php可能看起来像这样:

<?php
// Require composer autoloader
require __DIR__ . '/vendor/autoload.php';

use Auth0\SDK\JWTVerifier;

try {
  $verifier = new JWTVerifier([
    'supported_algs' => ['{HASHING_ALGORITHM}'],
    'valid_audiences' => ['{AUDIENCE}'],
    'authorized_iss' => ['{DOMAIN}']
  ]);

  $access_token = getBearerToken();
  $token_info = $verifier->verifyAndDecode($access_token);

  echo json_encode(array('date'=>'API Resource Data!!'));
}
catch(\Auth0\SDK\Exception\CoreException $e) {
  throw $e;
  echo json_encode(array('error'=>$e->getMessage()));
}

function getAuthorizationHeader() {
    $headers = null;

    if (isset($_SERVER['Authorization'])) {
        $headers = trim($_SERVER["Authorization"]);
    }
    else if (isset($_SERVER['HTTP_AUTHORIZATION'])) { //Nginx or fast CGI
        $headers = trim($_SERVER["HTTP_AUTHORIZATION"]);
    } elseif (function_exists('apache_request_headers')) {
        $requestHeaders = apache_request_headers();
        // Server-side fix for bug in old Android versions (a nice side-effect of this fix means we don't care about capitalization for Authorization)
        $requestHeaders = array_combine(array_map('ucwords', array_keys($requestHeaders)), array_values($requestHeaders));
        //print_r($requestHeaders);
        if (isset($requestHeaders['Authorization'])) {
            $headers = trim($requestHeaders['Authorization']);
        }
    }
    return $headers;
}

function getBearerToken() {
    $headers = getAuthorizationHeader();

    // HEADER: Get the access token from the header
    if (!empty($headers)) {
        if (preg_match('/Bearer\s(\S+)/', $headers, $matches)) {
            return $matches[1];
        }
    }
    return null;
}

让我们快速遍历这段代码的重要部分。

验证访问令牌

在授予对受保护资源的访问权限之前,您有责任验证传入的访问令牌。 这正是我们在以下代码段中所做的。 我们使用了JWTVerifier实用工具类来验证访问令牌。

try {
  $verifier = new JWTVerifier([
    'supported_algs' => ['{SIGNING_ALGORITHM}'],
    'valid_audiences' => ['{AUDIENCE}'],
    'authorized_iss' => ['{DOMAIN}']
  ]);

  $access_token = getBearerToken();
  $token_info = $verifier->verifyAndDecode($access_token);

  echo json_encode(array('date'=>'API Resource Data!!'));
}
catch(\Auth0\SDK\Exception\CoreException $e) {
  throw $e;
  echo json_encode(array('error'=>$e->getMessage()));
}

{SIGNING_ALGORITHM}应该替换为API> {您的API应用}>设置下的“ 签名算法”字段的值。

因此,如果您希望在Auth0服务中使用OAuth2流,这就是保护您的自定义API的方式。

结论

今天,我们经历了Auth0服务,该服务提供身份验证和授权即服务。 引入Auth0服务后,我们通过几个实际示例演示了如何将其与PHP应用程序集成。

请随时使用以下供稿发布您的建议和疑问!

翻译自: https://code.tutsplus.com/tutorials/authentication-and-authorization-using-auth0-in-php--cms-31134

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值