提供用户信息分类
译者:老葛 Eskalate科技公司
如果你在http://drupal.org拥有一个帐号,通过登录并点击“my account”链接,接着选择编辑标签(edit tab),你就可以看到提供关于用户信息的分类的效果。除了编辑你的帐号信息比如你的密码以外,你可以在其它分类中你还可以提供你的其它个人信息。在编写此书时,http://drupal.org支持编辑CVS信息、Drupal相关信息、个人信息、工作信息、以及接收新闻通讯的偏好。
通过使用profile.module或者用户钩子的分类操作,你可以添加像这些分类一样的信息分类;参看profile.module中的实现。
外部登陆
Drupal已经内置了对外部认证的支持,通过在一个模块中实现相应的钩子,就可以简单的将外部认证插入到Drupal中。Drupal进行外部认证时所走的流程概貌如图6-6所示。
如果没有启用提供外部认证(就是说,实现auth钩子)的模块,Drupal将把所有的用户名当作本地的用户名进行处理。所以joe和joe@example.com都被看做简单的字符串,而没有任何其它特殊含义。然而,如果启用了一个提供外部认证的模块,那么这两个用户名的处理流程就会非常不同了。
注意 Drupal在尝试外部认证以前,总是首先尝试以本地用户进行登录。
图6-6 Drupal的外部登陆流程
简单的外部认证
让我们实现一个非常简单的外部认证模块,一个使用简单用户名的公司使用这个模块。假定你的公司只雇用名为Dave的员工,而用户名基于第一个和最后一个名字(姓和名)指定。对于任何以dave开头的用户名都将通过该模块的认证,所以用户davebrown, davesmith, 和davejones都将能够成功的登录。
<?php
// $Id$
/**
* Implementation of hook_auth()
*/
function authdave_auth($username, $pass, $server) {
// Does username begin with 'dave'?
if (substr(drupal_strtolower($username, 0, 4 )) == 'dave') {
// Make a global variable to note that we did the authentication.
global $authdave_authenticated;
$authdave_authenticated = TRUE;
return TRUE;
}
else {
return FALSE;
}
}
如果一个用户在users表中不存在对应的记录,那么将会为其创建一个记录。然而,在登录过程中创建的用户没有为其提供e-mail地址,在Drupal默认的本地用户注册时则提供了e-mail地址,如果你的站点需要发送e-mail,那么这样简单的模块就不是一个可行的解决方案。你需要设置users表的mail列,这样你就有了一个与用户相关联的e-mail地址。为了做到这一点,你可以实现用户钩子(hook_user),给出插入操作时的逻辑,这样当插入一个新的用户时就会调用相应的逻辑:
/**
* Implementation of hook_user()
*/
function authdave_user($op, &$edit, &$account, $category = NULL) {
switch($op) {
case 'insert':
// New user was just added; if we did authentication,
// look up email address of user in a legacy database.
global $authdave_authenticated;
if ($authdave_authenticated) {
$email = mycompany_email_lookup($account->name);
// Set email address in the user table for this user.
db_query("UPDATE {users} SET mail = '%s' WHERE uid = %d", $email,
$account->uid);
}
break;
}
}
聪明的读者将会注意到,如果同时启用了Drupal的本地认证和我们的外部认证,那么就没有一种方式来让插入操作下面的代码告诉我们,用户是通过本地认证的还是通过外部认证的;所以我们在这里使用全局变量以指示我们的模块进行了认证。
使用提供的服务器进行外部认证
当一个用户使用joe@example.com格式的用户名开始登录时,我们需要依照更多的信息进行处理。Drupal内核包含了drupal.module,它提供了一个XML-RPC客户端,可用于连接到其它服务器上请求认证。例如,在网站http://groups.drupal.org上,你可以使用你在http://drupal.org上的用户名和密码进行登录。下面是我第一次登录时所发生的事情:
1.我使用用户名jvandyk@drupal.org和我的密码在groups.drupal.org上登录。、
2.groups.drupal.org检查本地用户数据库并且没有找到我。
3.groups.drupal.org检查authmap,也没能找到我。
4.由于在groups.drupal.org上启用了drupal.module,调用它的auth钩子。
5. drupal.module向http://drupal.org发送一个XML-RPC请求,并且询问,“在你这里是不是有一个名为jvandyk并且使用这个密码的用户?”
6. drupal.org回答道,“是的,它是一个热心的用户”。
7. groups.drupal.org为我在users表中添加一条记录(包括一个本地用户ID),在authmap表中也添加一条记录,这样当我下次登录时,只需要运行步骤1和步骤3就可以了。
当提供了一个服务器时,外部登陆的关键点在于authmap表。这个表包含了3个很重要的列:用户ID,外部的用户名,处理认证的模块的名字。在前面的例子中,我的用户ID可能是334,用户名是jvandyk@drupal.org,模块列的值为drupal,这是因为drupal module对我进行了认证,当我下次登录时仍然由它负责认证我。
注意 在这里,drupal.org为groups.drupal.org对我进行了认证。但是drupal.org没有将我的e-mail地址提供给groups.drupal.org。和我们本节中的简单外部认证例子一样,如果http://groups.drupal.org的维护者认为users表的mail列都有值的话,那么这将是一个非常愚蠢的想法。这里使用了一个随机生成的密码作为password列的值。
下面是drupal.module中auth钩子实现的简化版本,用来说明前面场景所用到的代码:
/**
* Implementation of hook_auth().
*/
function drupal_auth($username, $password, $server = FALSE) {
if (!empty($server)) {
// Ask remote server to attempt login for this username and password.
$result = xmlrpc("http://$server/xmlrpc.php", 'drupal.login', $username,
$password);
if ($result === FALSE) { // Authentication failed.
drupal_set_message(t('Error %code: %message', array(
'%code' => xmlrpc_errno(),
'%message' => xmlrpc_error_msg())), 'error');
return FALSE;
}
else {
return $result;
}
}
}
}
在认证服务器上(在前面的例子中就是http://drupal.org),为了响应服务器端drupal_auth()发出的XML-RPC请求,运行以下代码:
/**
* Callback function from drupal_xmlrpc() for authenticating remote clients.
*
* Remote clients are usually other Drupal instances.
*/
function drupal_login($username, $password) {
if (variable_get('drupal_authentication_service', 0)) {
if ($user = user_load(array(
'name' => $username,
'pass' => $password,
'status' => 1))) {
// Found an unblocked user so return user ID.
return $user->uid;
}
else {
return 0;
}
}
}
info钩子
如果你的模块实现了外部认证(也就是说,使用了auth钩子),那么你也应该实现info钩子。这个钩子提供了你模块的名字以及它的验证方法,当其它模块想知道有哪些验证方法可用时使用这一信息。例如,在user.module就用它构建用户登录页面所支持的认证方法列表:
/**
* Implementation of hook_info().
*/
function drupal_info($field = 0) {
$info['name'] = 'Drupal';
$info['protocol'] = 'XML-RPC';
if ($field) {
return $info[$field];
}
else {
return $info;
}
}
总结
读完本章后,你应该能够
• 理解用户在Drupal内部是如何表示的
• 理解如何使用不同的方式来存储与用户相关的信息
• 使用用户注册过程中的钩子,来获取一个正在注册的用户的更多信息。
• 使用用户登录过程中的钩子,在用户登录时运行你自己的代码
• 理解两种不同的外部认证方式的工作原理
• 实现你自己的外部认证模块
注意 更多关于外部认证的信息,参看ldap_integration.module, pubcookie.module, 和 sxip.module。