php身份证验证
在本文的第一部分中,我向您介绍了Sentry 2,逐步介绍了它的安装和配置,并提供了一些使用它为PHP应用程序实现身份验证工作流的示例。 除其他外,我演示了如何通过激活电子邮件来设置注册表。 实施密码重置工作流程; 以及创建一个支持创建,编辑和删除用户帐户的用户帐户管理器。
在第二篇也是最后一篇文章中,我将深入探讨Sentry 2权限模型,说明如何创建组,为其分配用户和权限以及使用权限检查有选择地启用应用程序功能。 我还将展示如何通过登录限制和临时用户停用来强化应用程序,并说明如何将Sentry 2与第三方身份验证服务(如Google和Twitter)结合使用。 进来,让我们开始吧!
分配用户权限
首先,出发点是:权限。 Sentry 2开箱即用地提供了一种合理的权限模型,既支持直接权限(直接分配给用户的权限)又支持间接权限(分配给组并由该组成员的用户继承的权限)。
为了说明它是如何工作的,请参见清单1,其中有一个创建用户帐户并为其分配权限的简单示例。
清单1.用户权限授予
<?php
// set up autoloader
require ('vendor\autoload.php');
// configure database
$dsn = 'mysql:dbname=appdata;host=localhost';
$u = 'sentry';
$p = 'g39ejdl';
Cartalyst\Sentry\Facades\Native\Sentry::setupDatabaseResolver(
new PDO($dsn, $u, $p));
// create user record
try {
$user = Cartalyst\Sentry\Facades\Native\Sentry::createUser(array(
'email' => 'test@example.com',
'password' => 'guessme',
'first_name' => 'Test',
'last_name' => 'User',
'activated' => true,
'permissions' => array('read' => 1, 'write' => 1)
));
} catch (Exception $e) {
echo $e->getMessage();
}
?>
如清单1所示,将权限分配给用户帐户非常简单:只需在传递给createUser()
方法的数组中添加一个“ permissions”键,然后指定一组权限。 要分配或更改现有用户帐户的权限,请检索相应的User对象,分配新的权限,然后将该对象save()
到数据库中。 权限名称是完全可自定义的,完全可以由您定义。 分配给权限名称的值表示是允许(1),拒绝(-1)还是继承(0)。
清单2通过允许您另外指定用户是否有权“查看”,“添加”,“编辑”或“删除”其他用户帐户,来更新您在本文第一部分中看到的用户帐户创建脚本。
清单2.交互式用户权限授予
<?php if (isset($_POST['submit'])): ?>
<?php
// set up autoloader
require ('vendor\autoload.php');
// configure database
$dsn = 'mysql:dbname=appdata;host=localhost';
$u = 'sentry';
$p = 'g39ejdl';
Cartalyst\Sentry\Facades\Native\Sentry::setupDatabaseResolver(new PDO($dsn, $u, $p));
// validate input and create user record
try {
$email = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL);
$fname = strip_tags($_POST['first_name']);
$lname = strip_tags($_POST['last_name']);
$password = strip_tags($_POST['password']);
$user = Cartalyst\Sentry\Facades\Native\Sentry::createUser(array(
'email' => $email,
'password' => $password,
'first_name' => $fname,
'last_name' => $lname,
'permissions' => $_POST['perms'],
'activated' => true,
));
echo 'User successfully created.';
} catch (Exception $e) {
echo 'User could not be created. ' . $e->getMessage();
}
?>
<?php else: ?>
<html>
<head></head>
<body>
<h1>Add User</h2>
<form action="<?php echo htmlentities($_SERVER['PHP_SELF']); ?>" method="post">
Email address: <br/>
<input type="text" name="email" /> <br/>
Password: <br/>
<input type="password" name="password" /> <br/>
First name: <br/>
<input type="text" name="first_name" /> <br/>
Last name: <br/>
<input type="text" name="last_name" /> <br/>
Permissions: <br/>
<input type="checkbox" name="perms[view]" value="1" />View
<input type="checkbox" name="perms[add]" value="1" />Add
<input type="checkbox" name="perms[edit]" value="1" />Edit
<input type="checkbox" name="perms[delete]" value="1" />Delete
<input type="submit" name="submit" value="Create" />
</form>
</body>
</html>
<?php endif; ?>
图1说明了修改后的用户帐户创建表单的外观。 在提交表单时,使用“权限”键将选中的权限分配给新创建的帐户。
图1.具有权限授予的帐户创建表单
验证用户权限
分配权限只是难题的第一步。 在应用程序的不同位置,通常将需要检查用户是否具有执行特定任务所需的权限,并拒绝对缺少足够特权的用户进行访问。 Sentry 2使用hasAccess()
方法使此操作变得容易,该方法可让您测试用户是否有权访问所需的权限。 考虑清单3,该清单修改了本文第一部分中的用户编辑工具,以仅在登录用户具有相应权限的情况下显示添加,编辑和删除链接。
清单3.用户权限检查
<?php
// set up autoloader
require ('vendor\autoload.php');
// configure database
$dsn = 'mysql:dbname=appdata;host=localhost';
$u = 'sentry';
$p = 'g39ejdl';
Cartalyst\Sentry\Facades\Native\Sentry::setupDatabaseResolver(new PDO($dsn, $u, $p));
try {
$currentUser = Cartalyst\Sentry\Facades\Native\Sentry::getUser();
if (!$currentUser->hasAccess('view')) {
throw new Exception ('You don\'t have permission to view this page.');
}
$users = Cartalyst\Sentry\Facades\Native\Sentry::findAllUsers();
?>
<html>
<head></head>
<body>
<h1>Users</h1>
<table border="1">
<tr>
<td>Email address</td>
<td>First name</td>
<td>Last name</td>
<td>Last login</td>
</tr>
<?php foreach ($users as $u): ?>
<?php $userArr = $u->toArray(); ?>
<tr>
<td><?php echo $userArr['email']; ?></td>
<td><?php echo isset($userArr['first_name']) ?
$userArr['first_name'] : '-'; ?></td>
<td><?php echo isset($userArr['last_name']) ?
$userArr['last_name'] : '-'; ?></td>
<td><?php echo isset($userArr['last_login']) ?
$userArr['last_login'] : '-'; ?></td>
<?php if ($currentUser->hasAccess('edit')): ?>
<td><a href="edit.php?id=<?php echo $userArr['id']; ?>">
Edit</a></td>
<?php endif; ?>
<?php if ($currentUser->hasAccess('delete')): ?>
<td><a href="delete.php?id=<?php echo $userArr['id']; ?>">
Delete</a></td>
<?php endif; ?>
</tr>
<?php endforeach; ?>
</table>
<?php if ($currentUser->hasAccess('add')): ?>
<a href="create.php">Add new user</a>
<?php endif; ?>
<body>
</html>
<?php
} catch (Exception $e) {
echo $e->getMessage();
}
?>
清单3设置了Sentry 2数据库连接,然后使用getUser()
方法检索与当前登录用户相对应的User对象。 然后,它在各个位置使用对象的hasAccess()
方法来决定用户是否有权查看用户帐户列表(“视图”)以及该列表中每个帐户旁边显示的编辑链接(“编辑”,“删除”或“添加”)。
清单4提供了另一个示例,该示例首先在允许删除帐户之前检查登录用户是否具有“删除”权限。
清单4.用户权限检查
<?php
if (isset($_GET['id'])) {
// set up autoloader
require ('vendor\autoload.php');
// configure database
$dsn = 'mysql:dbname=appdata;host=localhost';
$u = 'sentry';
$p = 'g39ejdl';
Cartalyst\Sentry\Facades\Native\Sentry::setupDatabaseResolver(new PDO($dsn, $u, $p));
try {
$currentUser = Cartalyst\Sentry\Facades\Native\Sentry::getUser();
if (!$currentUser->hasAccess('delete')) {
throw new Exception ('You don\'t have permission to view this page.');
}
// find user by id and delete
$id = strip_tags($_GET['id']);
$user = Cartalyst\Sentry\Facades\Native\Sentry::findUserById($id);
$user->delete();
echo 'User successfully deleted.';
} catch (Exception $e) {
echo $e->getMessage();
}
}
?>
建立群组
Sentry 2还允许您将用户分为几组。 如果要允许不同的用户级别使用不同的功能级别,则此功能特别有用。 组是使用createGroup()
方法创建的,如清单5所示。
清单5.组创建
<?php
// set up autoloader
require ('vendor\autoload.php');
// configure database
$dsn = 'mysql:dbname=appdata;host=localhost';
$u = 'sentry';
$p = 'g39ejdl';
Cartalyst\Sentry\Facades\Native\Sentry::setupDatabaseResolver(
new PDO($dsn, $u, $p));
// create group record
try {
$group1 = Cartalyst\Sentry\Facades\Native\Sentry::createGroup(array(
'name' => 'staff',
'permissions' => array(
'view' => 1,
'add' => 0,
'edit' => 1,
'delete' => 0
)
));
$group2 = Cartalyst\Sentry\Facades\Native\Sentry::createGroup(array(
'name' => 'managers',
'permissions' => array(
'view' => 1,
'add' => 1,
'edit' => 1,
'delete' => 1
)
));
} catch (Exception $e) {
echo $e->getMessage();
}
?>
清单5创建了两个新组,名为“ staff”和“ managers”。 “工作人员”组仅具有“查看”和“编辑”权限,而“管理人员”组也具有“编辑”和“删除”权限。
组记录存储在“组”表中。 使用DESC
命令查看此表的结构。
mysql> DESC groups;
图2说明了此表的结构。
图2. Sentry 2组表的结构
清单6演示了将用户添加到组的过程。
清单6.组的用户分配
<?php
// set up autoloader
require ('vendor\autoload.php');
// configure database
$dsn = 'mysql:dbname=appdata;host=localhost';
$u = 'sentry';
$p = 'g39ejdl';
Cartalyst\Sentry\Facades\Native\Sentry::setupDatabaseResolver(
new PDO($dsn, $u, $p));
// create user record
try {
$user = Cartalyst\Sentry\Facades\Native\Sentry::createUser(array(
'email' => 'test@example.com',
'password' => 'guessme',
'first_name' => 'Test',
'last_name' => 'User',
'activated' => true
));
$group = Cartalyst\Sentry\Facades\Native\Sentry::findGroupByName('managers');
$user->addGroup($group);
} catch (Exception $e) {
echo $e->getMessage();
}
?>
将用户添加到组是一个分为两个步骤的过程。 首先,使用findGroupByName()
方法检索一个Group对象,然后调用User对象的addGroup()
方法以将该组附加到用户帐户。
合并用户和组权限
组提供了一种方便的方式来隐式分配权限给用户。 只需将用户分配给组,该用户将自动继承该组的权限。 按照清单5中的示例,属于“ staff”组的用户将自动继承该组的“查看”和“编辑”权限,而属于经理组的用户将自动继承该组的权限,同时还“编辑”和“删除”权限”。
通过在创建或更新用户帐户时显式指定不同的值,可以覆盖用户的默认组权限。 考虑清单7,它说明了这一点。
清单7.清单7:组权限覆盖
<?php
// set up autoloader
require ('vendor\autoload.php');
// configure database
$dsn = 'mysql:dbname=appdata;host=localhost';
$u = 'sentry';
$p = 'g39ejdl';
Cartalyst\Sentry\Facades\Native\Sentry::setupDatabaseResolver(
new PDO($dsn, $u, $p));
// create user record
try {
$user = Cartalyst\Sentry\Facades\Native\Sentry::createUser(array(
'email' => 'john@example.com',
'password' => 'guessme',
'first_name' => 'John',
'last_name' => 'User',
'activated' => true,
'permissions' => array('edit' => '-1')
));
$group = Cartalyst\Sentry\Facades\Native\Sentry::findGroupByName('managers');
$user->addGroup($group);
} catch (Exception $e) {
echo $e->getMessage();
}
?>
在清单7中,用户“ john@example.com”属于“ managers”组,因此默认情况下将具有所有权限。 但是,在创建帐户时,已明确拒绝该帐户的“编辑”权限。 结果,用户将仅获得“查看”,“添加”和“删除”权限。
清单8演示了使用getMergedPermissions()
方法来检索用户的直接和间接合并权限。
清单8.合并的用户和组权限
<?php
// set up autoloader
require ('vendor\autoload.php');
// configure database
$dsn = 'mysql:dbname=appdata;host=localhost';
$u = 'sentry';
$p = 'g39ejdl';
Cartalyst\Sentry\Facades\Native\Sentry::setupDatabaseResolver(new PDO($dsn, $u, $p));
// if form submitted
if (isset($_POST['submit'])) {
try {
// validate input
$username = filter_var($_POST['username'], FILTER_SANITIZE_EMAIL);
$password = strip_tags(trim($_POST['password']));
// set login credentials
$credentials = array(
'email' => $username,
'password' => $password,
);
// authenticate
// if authentication fails, capture failure message
Cartalyst\Sentry\Facades\Native\Sentry::authenticate($credentials, false);
} catch (Exception $e) {
$failMessage = $e->getMessage();
}
}
// check if user logged in
if (Cartalyst\Sentry\Facades\Native\Sentry::check()) {
$currentUser = Cartalyst\Sentry\Facades\Native\Sentry::getUser();
}
?>
<html>
<head></head>
<body>
<?php if (isset($currentUser)): ?>
Logged in as <?php echo $currentUser->getLogin(); ?>.
<a href="logout.php">[Log out]</a> <br/>
Permissions: <?php echo implode(', ',
array_keys($currentUser->getMergedPermissions(), '1')); ?>
<?php else: ?>
<h1>Log In</h1>
<div><?php echo (isset($failMessage)) ?
$failMessage : null; ?></div>
<form method="post">
Username: <input type="text" name="username" /> <br/>
Password: <input type="password" name="password" /> <br/>
<input type="submit" name="submit" value="Log In" />
</form>
<?php endif; ?>
</body>
</html>
图3显示了清单8中计算的“ john@example.com”权限。
图3.用户权限显示
Sentry 2还提供了特殊的“超级用户”权限,该权限用作向用户分配“所有可用权限”的快捷方式。 清单9展示了它的运行。
清单9.超级用户权限授予
<?php
// set up autoloader
require ('vendor\autoload.php');
// configure database
$dsn = 'mysql:dbname=appdata;host=localhost';
$u = 'sentry';
$p = 'g39ejdl';
Cartalyst\Sentry\Facades\Native\Sentry::setupDatabaseResolver(
new PDO($dsn, $u, $p));
// create user record
try {
$user = Cartalyst\Sentry\Facades\Native\Sentry::createUser(array(
'email' => 'root@example.com',
'password' => 'guessme',
'first_name' => 'Root',
'last_name' => 'User',
'activated' => true,
'permissions' => array('superuser' => '1')
));
} catch (Exception $e) {
echo $e->getMessage();
}
?>
限制用户登录
Sentry 2还具有一个有趣的功能,可以提高应用程序的安全性:如果登录尝试失败的次数过多,则会自动限制登录。 在这种情况下,需要了解一些有趣的方法:
-
setAttemptLimit()
方法设置限制登录前允许的最大尝试次数。 -
setSuspensionTime()
方法设置应限制登录的分钟数。 -
getLoginAttempts()
方法检索登录尝试的次数。 -
clearLoginAttempts()
方法清除失败的登录尝试次数,并取消挂起登录帐户。 -
check()
方法检索登录的当前状态,无论是否受到限制。 -
suspend()
方法挂起登录帐户,unsuspend()
方法将其恢复为活动状态。 -
ban()
方法禁止登录帐户,unBan()
方法将其恢复为活动状态。
清单10显示了将限制添加到登录工作流程是多么容易。
清单10.登录限制
<?php
// set up autoloader
require ('vendor\autoload.php');
// configure database
$dsn = 'mysql:dbname=appdata;host=localhost';
$u = 'sentry';
$p = 'g39ejdl';
Cartalyst\Sentry\Facades\Native\Sentry::setupDatabaseResolver(new PDO($dsn, $u, $p));
// enable throttling
$throttleProvider = Cartalyst\Sentry\Facades\Native\Sentry::getThrottleProvider();
$throttleProvider->enable();
// if form submitted
if (isset($_POST['submit'])) {
try {
// validate input
$username = filter_var($_POST['username'], FILTER_SANITIZE_EMAIL);
$password = strip_tags(trim($_POST['password']));
// configure throttling
$throttle = $throttleProvider->findByUserLogin($username);
$throttle->setAttemptLimit(3);
$throttle->setSuspensionTime(5);
$credentials = array(
'email' => $username,
'password' => $password,
);
Cartalyst\Sentry\Facades\Native\Sentry::authenticate($credentials, false);
} catch (Exception $e) {
// all other authentication failures
$failMessage = $e->getMessage();
}
}
// check if user logged in
if (Cartalyst\Sentry\Facades\Native\Sentry::check()) {
$currentUser = Cartalyst\Sentry\Facades\Native\Sentry::getUser();
}
?>
<html>
<head></head>
<body>
<?php if (isset($currentUser)): ?>
Logged in as <?php echo $currentUser->getLogin(); ?>
<a href="logout.php">[Log out]</a>
<?php else: ?>
<h1>Log In</h1>
<div><?php echo (isset($failMessage)) ?
$failMessage : null; ?></div>
<form method="post">
Username: <input type="text" name="username" /> <br/>
Password: <input type="password" name="password" /> <br/>
<input type="submit" name="submit" value="Log In" />
</form>
<?php endif; ?>
</body>
</html>
清单10实现了您在前面的示例中看到的标准Sentry 2登录工作流程,但有两个明显的区别:
-
getThrottleProvider()
方法用于检索全局限制提供程序的实例。 此提供程序对象用作登录限制的主要控制点。 调用对象的enable()
方法将激活Sentry 2的内置限制功能。 - 节流对象的
findByUserLogin()
方法用于检索用户节流对象的实例。 对象的setAttemptLimit()
和setSuspensionTime()
方法用于配置登录限制的关键参数-尝试次数和暂停时间。
如果现在尝试使用错误的密码登录到应用程序,则在三次尝试失败后,您的帐户将自动暂停五分钟。 这对试图通过使用字典猜测密码的僵尸程序和自动脚本提供了可靠的响应。
与第三方网络集成(Twitter)
如您所见,Sentry 2提供了对存储在其自己的数据库中的用户进行身份验证所需的一切。 但是,如今,您通常希望您的应用程序还支持通过第三方OAuth提供程序(例如Google,Facebook或Twitter)登录。
Sentry Social 3是Sentry 2的一个附加组件,旨在与社交网络集成。 但是,此组件只能在商业许可下使用,因此,它可能不是最适合您的项目的组件。 因此,另一种解决方案是使用每个第三方提供商提供的开源OAuth库(在大多数情况下)。
放松,实际上并不像听起来那样复杂。 为了说明这一点,我将通过将Sentry 2与当今两个最受欢迎的社交网络(Twitter和Google+)集成在一起,向您展示一些不同的身份验证和帐户创建工作流程的示例。
如果您曾经尝试使用Twitter,Google或Facebook凭据登录到应用程序,则可能会注意到该应用程序能够访问该网络上的帐户详细信息,并使用该信息预先填写自己的注册表格。 清单11通过使用Zend Framework 1.x中的两个组件(特别是Zend_Oauth_Consumer和Zend_Json)来连接到Twitter API并检索身份验证用户的屏幕名称,演示了此工作流程。 然后,此信息将用于填充应用程序的本地帐户创建表单,该表单在提交后会在Sentry 2数据库中创建一个新的用户帐户。
清单11假定您在PHP包含路径中具有Zend Framework的工作副本。 注意,在使用清单11中的代码之前,必须首先在Twitter上注册您的应用程序,获取使用者密钥和机密(图4中的示例),然后将此信息插入到清单11中的适当位置。 请参阅相关信息的链接,Zend框架和用于管理应用Twitter API的开发者控制台。
图4. Twitter应用程序控制台设置
清单11.使用Twitter进行用户身份验证和配置文件检索
<?php
session_start();
if (isset($_POST['submit'])) {
// if form submitted
// use input to create user account
require ('vendor\autoload.php');
// configure database
$dsn = 'mysql:dbname=appdata;host=localhost';
$u = 'sentry';
$p = 'g39ejdl';
Cartalyst\Sentry\Facades\Native\Sentry::setupDatabaseResolver(new PDO($dsn, $u, $p));
try {
$email = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL);
$fname = strip_tags($_POST['name']);
$password = strip_tags($_POST['password']);
$user = Cartalyst\Sentry\Facades\Native\Sentry::createUser(array(
'email' => $email,
'password' => $password,
'first_name' => $fname,
));
echo 'User successfully created.';
} catch (Exception $e) {
echo 'User could not be created. ' . $e->getMessage();
}
} else {
// if form not submitted
// connect to Twitter via Oauth
// ask for user authorization and retrieve user's name via Twitter API
// pre-populate account creation form
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Oauth_Consumer');
Zend_Loader::loadClass('Zend_Json');
$config = array(
'callbackUrl' => 'http://yourhost/path/to/script.php',
'siteUrl' => 'https://api.twitter.com/oauth',
'consumerKey' => 'YOUR-CONSUMER-KEY',
'consumerSecret' => 'YOUR-CONSUMER-SECRET'
);
$consumer = new Zend_Oauth_Consumer($config);
// get access token
if (!empty($_GET) && isset($_SESSION['TWITTER_REQUEST_TOKEN'])) {
$token = $consumer->getAccessToken(
$_GET,
unserialize($_SESSION['TWITTER_REQUEST_TOKEN'])
);
$_SESSION['TWITTER_ACCESS_TOKEN'] = serialize($token);
}
// fetch a request token
if (!isset($_SESSION['TWITTER_REQUEST_TOKEN'])) {
$token = $consumer->getRequestToken();
$_SESSION['TWITTER_REQUEST_TOKEN'] = serialize($token);
$consumer->redirect();
}
// use access token to connect to Twitter API
// decode response and retrieve user's screen name
// generate form with name pre-filled
if (isset($_SESSION['TWITTER_ACCESS_TOKEN'])) {
$token = unserialize($_SESSION['TWITTER_ACCESS_TOKEN']);
$client = $token->getHttpClient($config);
$client->setUri('https://api.twitter.com/1.1/account/verify_credentials.json');
$client->setMethod(Zend_Http_Client::GET);
$response = $client->request();
$data = Zend_Json::decode($response->getBody());
?>
<html>
<head></head>
<body>
<form method="post">
Name: <input type="text" name="name"
value="<?php echo $data['name']; ?>" /> <br/>
Email address: <input type="text" name="email" value="" /> <br/>
Password: <input type="password" name="password" value="" /> <br/>
<input type="submit" name="submit" value="Create Account" />
</form>
</body>
</html>
<?php
}
}
?>
清单11从开始一个新的会话开始,该会话将用作在身份验证过程中接收到的OAuth访问和请求令牌的存储容器。 然后,它使用Zend Framework自动加载器加载所需的类并初始化新的Zend_Oauth_Consumer对象。 然后,该代码实现标准的OAuth工作流程,首先获取请求令牌,然后将用户重定向到Twitter以登录,认证并获取访问令牌。
有了访问令牌,清单11现在可以向Twitter API的/verify_credentials
端点发出经过身份验证的请求。 对此请求的响应是一个JSON文档,其中包含简短的用户个人资料,包括用户的屏幕名称。 现在,可以很容易地解码JSON数据,从中提取用户的屏幕名称,并使用此信息来预先填充帐户创建表单。 用户现在只需简单地添加他或她的电子邮件地址并提交表单,这将启动Sentry 2并使用您之前看到的createUser()
方法创建一个用户帐户。
图5显示了正在进行的Twitter身份验证的示例以及由此产生的帐户创建表单,其中用户的屏幕名称已经从Twitter API中填充。
图5. Twitter身份验证过程
如果您想知道清单11为什么也不能用用户的电子邮件地址预先填充帐户创建表单,原因很简单:Twitter不会通过其API公开电子邮件地址。 由于电子邮件地址是Sentry 2使用的主要帐户标识符,因此用户必须手动请求并输入此信息才能完成帐户创建过程。 不过,请不要担心,这是特定于Twitter的约束,清单12(使用Google+)对此有所不同。
与第三方网络集成(Google)
Google+作为社交网络正变得越来越流行,并且Google还提供了PHP OAuth客户端的可靠实现。 该客户端可以免费下载,可以连接到所有Google API,包括Google+ API。 但是,在使用以下清单中的代码之前,您必须首先在Google上注册您的应用程序,获取客户ID和密码,然后将此信息插入每个清单中的适当位置(图6中的示例)。 请参阅相关信息的链接,谷歌PHP OAuth用户端和谷歌的API控制台。
图6. Google应用程序控制台设置
清单12提供了一个示例,该示例针对Google OAuth服务进行身份验证,从Google+ API检索经过身份验证的用户名和电子邮件地址,并使用此信息自动创建新的Sentry 2用户帐户。
清单12.使用Google进行用户身份验证和帐户创建
<?php
// load required classes
require_once 'vendor/google-api-php-client/src/Google_Client.php';
require_once 'vendor/google-api-php-client/src/contrib/Google_Oauth2Service.php';
require_once 'vendor/google-api-php-client/src/contrib/Google_PlusService.php';
require_once 'vendor\autoload.php';
// configure database
$dsn = 'mysql:dbname=appdata;host=localhost';
$u = 'sentry';
$p = 'g39ejdl';
Cartalyst\Sentry\Facades\Native\Sentry::setupDatabaseResolver(new PDO($dsn, $u, $p));
// start session
session_start();
// initialize OAuth 2.0 client
// set scopes
$client = new Google_Client();
$client->setApplicationName('Project X');
$client->setClientId('YOUR-CLIENT-ID');
$client->setClientSecret('YOUR-CLIENT-SECRET');
$client->setRedirectUri('http://yourhost/path/to/script.php');
$client->setScopes(array(
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/plus.login'
));
$oauth2Service = new Google_Oauth2Service($client);
$plusService = new Google_PlusService($client);
// if code received, authenticate and store token in session
if (isset($_GET['code'])) {
$client->authenticate();
$_SESSION['token'] = $client->getAccessToken();
$redirect = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
header('Location: ' . filter_var($redirect, FILTER_SANITIZE_URL));
exit;
}
// if token available in session, set token in client
if (isset($_SESSION['token'])) {
$client->setAccessToken($_SESSION['token']);
}
// if token available in client, access API
// get user name and email address
// generate random password
// create user in Sentry2 database and notify user with email
if ($client->getAccessToken()) {
$userinfo = $oauth2Service->userinfo->get();
$profile = $plusService->people->get('me');
$email = filter_var($userinfo['email'], FILTER_SANITIZE_EMAIL);
$fname = $profile['name']['givenName'];
$lname = $profile['name']['familyName'];
$password = substr(md5(rand()),0,7);
try {
$user = Cartalyst\Sentry\Facades\Native\Sentry::createUser(array(
'email' => $email,
'password' => $password,
'first_name' => $fname,
'last_name' => $lname,
'activated' => true,
));
$body = <<<EOM
Your account was successfully created. Please log in with the credentials below:
Email: $email
Password: $password
EOM;
@mail($email, 'Account created successfully', $body);
} catch (Exception $e) {
$failMessage = $e->getMessage();
}
$_SESSION['token'] = $client->getAccessToken();
} else {
$authUrl = $client->createAuthUrl();
}
?>
<html>
<head></head>
<body>
<?php if (isset($authUrl)): ?>
<a href='<?php echo $authUrl; ?>'>Register with Google+</a>
<?php else: ?>
<div><?php echo (isset($failMessage)) ?
$failMessage : "Account created for '$fname $lname'.
Credentials: $email / $password"; ?></div>
<?php endif; ?>
</body>
</html>
清单12首先加载GooglePHP OAuth库,建立Sentry 2数据库连接并开始一个新PHP会话。 然后,它将初始化Google OAuth客户端,设置客户端ID和从Google API控制台获取的密码,并且重要的是,定义客户端的范围。 最后,它初始化两个服务对象,一个用于OAuth服务,一个用于Google+服务,并使用客户端的createAuthUrl()
方法创建一个身份验证URL。
按照标准的OAuth工作流程,单击身份验证URL的链接会将用户带到Google身份验证页面(图7)。 用户对应用程序进行身份验证并确认其有权访问的数据之后,将生成访问令牌并将其存储在会话中。 通过此访问令牌,客户端可以访问选定的Google API。
图7. Google身份验证过程
使用访问令牌,服务对象可以获取身份验证用户的配置文件信息,例如名字,姓氏和电子邮件地址。 然后,此信息与自动生成的随机密码结合在一起,以在Sentry 2数据库中创建并激活新的用户帐户。 然后使用PHP的mail()
函数将包括自动生成的密码在内的帐户信息通过电子邮件发送给用户。
此方法的一种变体是从第三方网络信息创建用户帐户,然后手动将用户登录到应用程序。 清单13是遵循此方法的清单12的修订版。
清单13.用户身份验证和使用Google登录
<?php
// load required classes
require_once 'vendor/google-api-php-client/src/Google_Client.php';
require_once 'vendor/google-api-php-client/src/contrib/Google_Oauth2Service.php';
require_once 'vendor/google-api-php-client/src/contrib/Google_PlusService.php';
require_once 'vendor\autoload.php';
// configure database
$dsn = 'mysql:dbname=appdata;host=localhost';
$u = 'sentry';
$p = 'g39ejdl';
Cartalyst\Sentry\Facades\Native\Sentry::setupDatabaseResolver(new PDO($dsn, $u, $p));
// start session
session_start();
// initialize OAuth 2.0 client
// set scopes
$client = new Google_Client();
$client->setApplicationName('Project X');
$client->setClientId('YOUR-CLIENT-ID');
$client->setClientSecret('YOUR-CLIENT-SECRET');
$client->setRedirectUri('http://yourhost/path/to/script.php');
$client->setScopes(array(
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/plus.login'
));
$oauth2Service = new Google_Oauth2Service($client);
$plusService = new Google_PlusService($client);
// if code received, authenticate and store token in session
if (isset($_GET['code'])) {
$client->authenticate();
$_SESSION['token'] = $client->getAccessToken();
$redirect = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
header('Location: ' . filter_var($redirect, FILTER_SANITIZE_URL));
exit;
}
// if token available in session, set token in client
if (isset($_SESSION['token'])) {
$client->setAccessToken($_SESSION['token']);
}
// if logout request, destroy session and revoke token
if (isset($_REQUEST['logout'])) {
unset($_SESSION['token']);
$client->revokeToken();
Cartalyst\Sentry\Facades\Native\Sentry::logout();
}
// if token available in client, access API
// get user name and email address
// check if user present in Sentry2 database and auto-login
// or create new user account and log user in
if ($client->getAccessToken()) {
$userinfo = $oauth2Service->userinfo->get();
$profile = $plusService->people->get('me');
$email = $userinfo['email'];
$fname = $profile['name']['givenName'];
$lname = $profile['name']['familyName'];
try {
try {
$user = Cartalyst\Sentry\Facades\Native\Sentry::findUserByLogin($email);
} catch (Cartalyst\Sentry\Users\UserNotFoundException $e) {
$password = substr(md5(rand()),0,7);
$user = Cartalyst\Sentry\Facades\Native\Sentry::createUser(array(
'email' => $email,
'password' => $password,
'first_name' => $fname,
'last_name' => $lname,
'activated' => true,
));
$createdMessage = "Account created for '$fname $lname'.
Credentials: $email / $password";
}
Cartalyst\Sentry\Facades\Native\Sentry::login($user, false);
if (Cartalyst\Sentry\Facades\Native\Sentry::check()) {
$currentUser = Cartalyst\Sentry\Facades\Native\Sentry::getUser();
}
} catch (Exception $e) {
$failMessage = $e->getMessage();
}
$_SESSION['token'] = $client->getAccessToken();
} else {
$authUrl = $client->createAuthUrl();
}
?>
<html>
<head></head>
<body>
<?php if (isset($authUrl)): ?>
<a href='<?php echo $authUrl; ?>'>Log in with Google+</a>
<?php else: ?>
<div><?php echo (isset($failMessage)) ?
$failMessage : null; ?></div>
<div><?php echo (isset($createdMessage)) ?
$createdMessage : null; ?></div>
Logged in as <?php echo $currentUser->getLogin(); ?>.
[<a href='?logout'>Log out</a>]
<?php endif; ?>
</body>
</html>
大多数清单13中的代码是一样的,在清单12 。 主要区别在于,在完成Google身份验证过程后,脚本将检查是否已经存在具有相同电子邮件地址的Sentry 2帐户,如果不存在,则会创建一个。 用户帐户出现在系统上之后,Sentry 2 login()
方法用于手动进行身份验证并将用户登录到应用程序。 还支持注销工作流,该工作流将用户从应用程序中注销,并撤消Google API访问令牌。
摘要
如这些示例所示,Sentry 2为PHP Web应用程序中的身份验证和粒度访问控制提供了功能全面的框架。 通过添加容易获得的开源库,可以将其与第三方社交网络和身份验证提供程序集成,从而以最小的开发工作量满足各种常见用例。 下次您要构建Web应用程序时,自己尝试一下,看看您的想法!
翻译自: https://www.ibm.com/developerworks/opensource/library/se-sentry2/index.html
php身份证验证