第6章 Drupal用户

老葛的Drupal培训班 Think in Drupal

用户是使用Drupal的重要原因。Drupal可以帮助用户创建一个在线社区,在上面大家可以一起协作,交流。在本章中,我们将揭开Drupal用户内幕,看一下如何验证用户,用户的登陆,还有用户的内部表示。首先,我们从检查对象$user是什么以及它是如何构造的开始。然后我们进一步讲述用户注册,用户登录,用户验证的流程。最后我们讲述了如何将一个现有的验证系统例如LDAP(轻量级目录访问协议)和Pubcookie与Drupal集成。
 

Drupal版本:

对象$user

老葛的Drupal培训班 Think in Drupal

用户为了登录,必须启用cookies。一个关闭了cookie的用户仍然可以以匿名的身份与Drupal进行交互。
     在引导指令流程的会话阶段,Drupal创建了一个全局$user对象,用来作为当前用户的标识。如果用户没有登录(这样就没有一个会话cookie),那么它将被当作匿名用户对待。创建匿名用户的代码如下所示(位于bootstrap.inc):
 
function drupal_anonymous_user($session = '') {
    $user = new stdClass();
    $user->uid = 0;
    $user->hostname = ip_address();
    $user->roles = array();
    $user->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
    $user->session = $session;
    $user->cache = 0;
    return $user;
}
 
    另一方面,如果用户当前登录了,那么可以通过使用用户的ID来关联表users和sessions,以创建对象$user。两个表中的所有字段都被放到了对象$user中。
 
注意:用户的ID是在用户注册时或者管理员创建用户时所分配的一个整数。这个ID是users表中的主键。
 
    通过向index.php中添加代码global $user;print_r($user);可以很容易的查看$user对象。下面是一个登录用户对象$user的通常结构:
 
stdClass Object (
    [uid]       => 2
    [name]    => Joe Example
    [pass]      => 7701e9e11ac326e98a3191cd386a114b
    [mail]      => joe@example.com
    [mode]      => 0
    [sort]      => 0
    [threshold] => 0
    [theme]     => bluemarine
    [signature] => Drupal rocks!
    [created]  => 1201383973
    [access]    => 1201384439
    [login]     => 1201383989
    [status]    => 1
    [timezone] => -21600
    [language] =>
    [picture]  => sites/default/files/pictures/picture-1.jpg
    [init]      => joe@example.com
    [data]      =>
    [roles]     => Array ( [2] => authenticated user )
    [sid]       => fq5vvn5ajvj4sihli314ltsqe4
    [hostname] => 127.0.0.1
    [timestamp] => 1201383994
    [cache]     => 0
    [session]  => user_overview_filter|a:0:{}
)
 
在上面显示的$user对象中,斜体字段意味着数据来自于sessions表。表6-1解释了$user对象的组成部分:
 
6-1 $user的组成部分
组成                     描述
来自于表users
uid               用户的ID.它是表users的主键,并在Drupal中是唯一的。
name              用户的用户名,当用户登录时输入
pass              用户的MD5哈希密码,当用户登录时进行对比。由于没有保存用户的原始真实密码,所以密码只能被重置,不能被恢复。
mail              用户当前的email地址
 
mode,sort和       特定于用户的评论浏览喜好
threshold
 
theme             如果启用了多个主题,这个代表用户选择的主题。如果用户主题未被安装,Drupal将其转到站点的默认主题。
signature         用户进入他/她的账号页面时所使用的签名。当用户添加一个评论时使用,并且只有当评论模块(comment module)启用时才可见。
created           用户账号创建时的Unix时间戳
access            用户最近一次访问的Unix时间戳
login             用户最近一次成功登录的Unix时间戳
status            1表示良好,0表示被拒绝访问的用户
timezone          用户时区与GMT之间的差异,以秒为单位
language          用户的默认语言。只有在站点上启用了多语言,并且用户通过编辑账号喜好选择了一个语言时,才不为空。
picture           与用户账号相联系的图像文件的路径
init              用户注册时提供的初始email地址
data              由模块存储的任何数据都可放置在这里(参看下一节,“向$user对象存储数据”)
来自于表user_roles
roles             分配给当前用户的角色
来自于表session
sid               通过PHP分配给当前用户会话的会话ID
hostname          用户浏览当前页面时所使用的IP地址
timestamp         一个Unix时间戳,表示用户的浏览器最后一次接收一个完整页面的时间
cache             一个用于per-user caching(参看 includes/cache.inc)的时间戳
session           在用户会话期间,模块可以向这里存储任意的数据。
 

Drupal版本:

向对象$user中存储数据

老葛的Drupal培训班 Think in Drupal

users包含了一个名为data的字段,用于存储保存序列化数组中的额外信息。如果你向对象$user中添加自己的数据,可以通过使用user_save()将数据存储在这一字段上。
 
// Add user's disposition.
global $user;
$extra_data = array('disposition' => t('Grumpy'));
user_save($user, $extra_data);
 
    现在,对象$user拥有了一个永久属性:
global $user;
print $user->disposition;
 
Grumpy
 
    尽管这种方式很方便,但是在用户登录和初始化对象$user时,由于以这种方式存储的数据需要反序列化,这样会增加更多的开销。因此,不加考虑就向$user中放入大量的数据将会引起一个性能瓶颈。一个可选的并且是更好的方法是,在$user加载时向它添加属性方法,将会在“在加载时向对象$user添加数据”一节进行讨论。
 

Drupal版本:

测试用户是否登录了

老葛的Drupal培训班 Think in Drupal

在请求期间,测试用户是否登录的标准方式,是检查$user->uid是否为0。Drupal有个名为user_is_logged_in()的函数可用来检查登录用户(还有一个相应的user_is_anonymous()函数用来检查匿名用户):
if (user_is_logged_in()) {
    $output = t('User is logged in.');
else {
    $output = t('User is an anonymous user.');
}

Drupal版本:

hook_user()入门

老葛的Drupal培训班 Think in Drupal

在你的模块中实现钩子hook_user(),它可以使你能够对用户账号进行不同的操作,以及修改$user对象。让我们看一下这个函数的签名:
 
function hook_user($op, &$edit, &$account, $category = NULL)
 
参数$op用来描述对用户账号所进行的当前操作,它可以有多个不同的值:
 
• after_update:在$user被保存到数据库中以后调用。
 
• categories:返回一个关于分类的数组,当用户编辑帐号时,这些分类被当作Drupal菜单本地任务(一般显示为可点击的标签)。这些实际上就是Drupal的菜单项。实现示例可参看profile.module中的profile_categories()。
 
• delete: 刚刚从数据库中删除了一个用户。这给了模块一个机会,用来从数据库中删除与该用户相关的信息。
 
• form:在被显示的用户编辑表单中,插入一个额外的表单字段元素。
 
• insert:新的用户帐号的记录已被保存到数据库中;将要保存$user->data和分配角色。之后,将会加载完整的$user对象。
 
• load:已成功加载用户帐号。模块可以向$user对象添加额外的信息(通过参数$account的引用传递,将信息传递给用户钩子)。
 
• login:用户已经成功登录。
 
• logout:用户刚刚退出登录,他/她的会话已被销毁。
 
• register:用户帐号注册表单将被显示。模块可以向表单中添加额外的表单元素。
 
• submit: 用户编辑表单已被提交。在帐号信息发送给user_save()以前,可对其进行修改。
 
• update: 已存在的用户帐号(修改后)将要被保存到数据库中。
 
• validate: 用户帐号已被修改。模块应该验证它的自定义数据,并生成必要的错误消息。
 
• view: 正在显示用户的帐号信息。模块应该返回它要显示的自定义信息,作为$user->content的结构化元素。查看操作最后调用theme_user_profile()来生成用户个人资料页面(更多详细可参看下面一节)。
 
     参数$edit是一个数组,当用户帐号正被创建或者更新时,所提交的表单数值构成了这一数组。注意它是通过引用传递的,所以你对它所做的任何修改都将修改实际的表单数值。
 
    $account对象(实际上就是一个$user对象)也是通过引用传递的,所以你对它所做的任何修改都将修改实际的$user信息。
 
    参数$category是正被编辑的当前用户帐号的分类。可以把分类看作用户相关的单独的信息组合。例如,如果你登录drupal.org,访问你的“我的账号”页面,并点击编辑标签,你将看到单独的分类,比如账号设置,个人信息,邮件订阅等等。
 
警告 不要混淆了hook_user()中的参数$account和全局变量$user对象。参数$account是当前正被操作的帐号的用户对象。而全局变量$user对象是当前登录的用户。通常,但不是绝对,两者是一样的。
 

Drupal版本:

理解hook_user('view')

老葛的Drupal培训班 Think in Drupal

模块可以使用hook_user('view')来向用户个人资料页面添加信息(例如,你在http://example.com/?q=user/1看到的;参看图6-1)
6-1 用户个人资料页面,这里日志模块和用户模块通过实现hook_user('view')来向该页面添加额外信息
 
    让我们看一下日志模块是如何向这一页面添加它的信息的:
 
/**
* Implementation of hook_user().
*/
function blog_user($op, &$edit, &$user) {
    if ($op == 'view' && user_access('create blog entries', $user)) {
        $user->content['summary']['blog'] = array(
            '#type' => 'user_profile_item',
            '#title' => t('Blog'),
            '#value' => l(t('View recent blog entries'), "blog/$user->uid",
                array('title' => t("Read @username's latest blog entries.",
                array('@username' => $user->name)))),
            '#attributes' => array('class' => 'blog'),
        );
    }
}
    查看操作向$user->content中加入了一些信息.用户资料信息组织成类别,而每个类别表示一个页面,里面包含了关于用户的信息.在图6-1中,只有一个名为History的类别.外部的数组的键应该对应于类别名,在前面的例子中,键的名字为summary,它对应于History类别(诚然,如果键的名字与类别名相同的话,那么会更好一些).内部数组应该有一个唯一的文本键(在这里为blog),它有4个元素#type, #title, #value, 和 #attributes。类型user_profile_item的主题层对应于modules/user/user-profile-item.tpl.php。通过对比代码片断与图6-1,你可以看到这些元素是如何显示的。图6-2给出了$user->content数组的内容,它生成了如图6-1的页面。
6-2. $user->content的结构
 
    你的模块还可以通过实现hook_profile_alter(),在个人资料项被主题化以前,来操作个人资料项。下面这个的例子,简单的从你的用户个人资料页面删除了日志项目。我们这里假定这个函数是模块hide.module中的:
 
/**
* Implementation of hook_profile_alter().
*/
function hide_profile_alter(&$account) {
    unset($account->content['summary']['blog']);
}
 

Drupal版本:

用户注册流程

默认情况下,Drupal站点的用户注册,只需要一个用户名和一个有效的e-mail地址就可以了。模块可以通过实现用户钩子,来向用户注册表单中添加它们自己的字段。让我们编写一个名为legalagree.module的模块,它提供了一个便捷的方式,以使你的站点适应今天这个法制社会。

 
    首先在sites/all/modules/custom创建一个名为legalagree的文件夹,并向legalagree目录中添加一下文件(参看列表6-1和6-2)。接着通过“管理➤站点构建➤模块”来启用模块。
 
列表 6-1. legalagree.info
; $Id$
name = Legal Agreement
description = Displays a dubious legal agreement during user registration.
package = Pro Drupal Development
core = 6.x
 老葛的Drupal培训班 Think in Drupal

Drupal版本:

用户注册流程(1)

列表 6-2. legalagree.module

<?php
// $Id$
 
/**
 * @file
 * Support for dubious legal agreement during user registration.
 */
 
/**
 * Implementation of hook_user().
 */
function legalagree_user($op, &$edit, &$user, $category = NULL) {
     switch($op) {
     // User is registering.
          case 'register':
              // Add a fieldset containing radio buttons to the
              // user registration form.
              $fields['legal_agreement'] = array(
                   '#type' => 'fieldset',
                   '#title' => t('Legal Agreement')
              );
              $fields['legal_agreement']['decision'] = array(
                   '#type' => 'radios',
                   '#description' => t('By registering at %site-name, you agree that at any time, we (or our surly, brutish henchmen) may enter your place of residence and smash your belongings with a ball-peen hammer.',array('%site-name' => variable_get('site_name', 'drupal'))),
                   '#default_value' => 0,
                   '#options' => array(t('I disagree'), t('I agree'))
          );
          return $fields;
    
     // Field values for registration are being checked.
     case 'validate':
          // Make sure the user selected radio button 1 ('I agree').
          // The validate op is reused when a user updates information on
          // the 'My account' page, so we use isset() to test whether we are
          // on the registration page where the decision field is present.
          if (isset($edit['decision']) && $edit['decision'] != '1') {
              form_set_error('decision', t('You must agree to the Legal Agreement before registration can be completed.'));
          }
          break;
    
     // New user has just been inserted into the database.
     case 'insert':
          // Record information for future lawsuit.
          watchdog('user', t('User %user agreed to legal terms', array('%user' => $user->name)));
          break;
     }
}
 
    在注册表单创建期间,在表单验证期间,还有在用户记录被插入到数据库中以后,都要调用user钩子。我们这个简单的模块将生成类似于图6-2所示的注册表单。
 
6-2 一个修改了的用户注册表单
 老葛的Drupal培训班 Think in Drupal

Drupal版本:

使用profile.module来收集用户信息

老葛的Drupal培训班 Think in Drupal

如果你想扩展用户注册表单以收集用户信息,在你打算编写自己的模块以前,你可以先试用一下profile.module。它允许你创建任意的用来收集数据的表单,在用户注册表单上定义信息是否是必须的(或者收集的),指定信息是公开的还是私有的。另外,它允许管理员定义页面,这样就可以根据用户的个人资料选项,使用一个由“站点URL” +profile/” + “个人资料字段的名字” +“值“构建的URL,来查看用户了。
 
    例如,如果你定义了一个名为profile_color的文本字段,你可以使用http://example.com/?q=profile/profile_color/black,来查看所有选择了黑色作为他们喜欢颜色的用户。或者假定你正在创建一个会议网站,并负责为参加者计划宴会。你可以定义一个名为profile_vegetarian的复选框,以作为个人资料字段,并可在http://example.com/?q=profile/profile_vegetarian(注意,对于复选框字段,值已被隐含,因此这里忽略了它)查看所有的素食用户;也就是说,这里没有向URL中追加一个值,而在前面的profile_color后面,则追加了black。
    在Drupal官方网站http://drupal.org上可以找到一个实际中的例子,参加马萨诸塞州波士顿2008年Drupal会议的用户列表,可以使用地址 http://drupal.org/profile/conference-boston-2008来查看(这里,字段名前面没有加前缀“profile_ “)。
 
提示 只有在个人资料字段设置中填充了字段Page的标题时,个人资料总结页面的自动创建才正常工作,但它不适用于textarea,URL,或者日期字段。
 

Drupal版本:

登录流程

当用户填完登录表单(一般位于http://example.com/?q=user 或者在一个区块中)并点击登录按钮时,登录流程开始。

 
    登录表单的验证程序检查用户名是否被封了,无论是根据访问规则拒绝访问,还是由于用户输入了一个错误的用户名或密码。如果任何一种情况发生了,都会及时的通知用户,告诉他为什么无法登录。
 
注意 Drupal中可以使用本地和外部两种方式进行认证。外部认证系统的例子包括OpenID,LDAP, Pubcookie,以及其它。一种外部认证类型是分布式认证,在这里来自于一个Drupal站点的用户可以登录到另一个Drupal站点(参看site_network模块,http://drupal.org/project/site_network)。
 
    Drupal将首先尝试从本地登录,在users表中查找是否存在一个与用户名和密码哈希值相匹配的记录。本地登录成功,则会调用两个用户钩子(load 和 login),在你的模块中可以实现这些钩子,如图6-4所示。
 
6-4 本地用户登录的执行路径
 老葛的Drupal培训班 Think in Drupal

Drupal版本:

在加载时,向$user对象添加数据

老葛的Drupal培训班 Think in Drupal

通过调用user_load(),从数据库中成功的加载一个$user对象时,将调用user钩子的加载(load)操作。当一个用户登录(或退出)时,当从一个节点取回作者信息时,或者一些其它情况时,都会调用这一加载(load)操作。
 
注意 由于触发user钩子非常耗费资源,所以在为一个请求实例化当前$user对象时(参看前面的“$user对象“部分),并没有调用user_load()。如果你正在编写你自己的模块,如果你调用的函数,需要完整的加载$user对象, 那么在该函数的前面,你首先需要调用user_load()函数,除非你能保证已经完整加载了$user对象。
 
    让我们创建一个名为“loginhistory”的模块,用来保存用户登录的历史记录。我们在用户的“我的帐号”页面显示用户已经登录的次数。在sites/all/modules/custom/下面创建一个名为loginhistory的文件夹,并添加列表6-3到6-5中的文件。首先是loginhistory.module。
 
列表 6-3. loginhistory.info
; $Id$
name = Login History
description = Keeps track of user logins.
package = Pro Drupal Development
core = 6.x
 

Drupal版本:

在加载时,向$user对象添加数据(1)

老葛的Drupal培训班 Think in Drupal

为了存储登录信息,我们需要使用一个.install文件来创建数据库表,所以我们创建了sites/all/modules/custom/loginhistory.install文件。
 
列表 6-4. loginhistory.install
<?php
// $Id$
 
/**
 * Implementation of hook_install().
 */
function loginhistory_install() {
    // Create tables.
    drupal_install_schema('loginhistory');
}
 
/**
 * Implementation of hook_uninstall().
 */
function loginhistory_uninstall() {
    // Remove tables.
    drupal_uninstall_schema('loginhistory');
}
 
/**
 * Implementation of hook_schema().
 */
function loginhistory_schema() {
    $schema['login_history'] = array(
        'description' => t('Stores information about user logins.'),
        'fields' => array(
            'uid' => array(
                'type' => 'int',
                'unsigned' => TRUE,
                'not null' => TRUE,
                'description' => t('The {user}.uid of the user logging in.'),
            ),
            'login' => array(
                'type' => 'int',
                'unsigned' => TRUE,
                'not null' => TRUE,
                'description' => t('Unix timestamp denoting time of login.'),
            ),
        ),
        'index' => array('uid'),
    );
   
    return $schema;
}
 
列表 6-5. loginhistory.module
<?php
// $Id$
 
/**
 * @file
 * Keeps track of user logins.
 */
 
/**
 * Implementation of hook_user().
 */
function loginhistory_user($op, &$edit, &$account, $category = NULL) {
    switch($op) {
        // Successful login.
        case 'login':
            // Record timestamp in database.
            db_query("INSERT INTO {login_history} (uid, login) VALUES (%d, %d)",$account->uid, $account->login);
            break;
 
        // $user object has been created and is given to us as $account parameter.
        case 'load':
            // Add the number of times user has logged in.
            $account->loginhistory_count = db_result(db_query("SELECT COUNT(login) AS count FROM {login_history} WHERE uid = %d", $account->uid));
            break;
 
        // 'My account' page is being created.
        case 'view':
            // Add a field displaying number of logins.
            $account->content['summary']['login_history'] = array(
                '#type' => 'user_profile_item',
                '#title' => t('Number of Logins'),
                '#value' => $account->loginhistory_count,
                '#attributes' => array('class' => 'login-history'),
                '#weight' => 10,
            );
            break;
    }
}
 
    在安装了这个模块以后,对于每次成功的用户登录,都将调用user钩子的login操作,在这个钩子里面,模块将向数据库表login_history插入一条记录。在加载$user对象时,将会调用用户加载钩子,此时模块将把用户的当前登录次数添加$user->loginhistory_count中。当用户查看“我的帐号”页面时,登录次数将显示出来,如图6-5所示。
6-5 追踪用户的登录历史
 
注意 当你在你的模块中为对象$user或$node添加属性时,在属性名前面最好加上前缀,以避免命名空间的冲突。这就是为什么这里使用$account->loginhistory_count来代替$account->count的原因。
 
 
    尽管在“我的帐号”页面,我们显示了我们添加到$user上的额外信息,记住由于$user对象是全局变量,所以其它模块也能访问它。我们留给读者一个非常有用的联系,为了安全起见,来修改前面的模块,在左(或右)边栏的区块中提供一个格式美观的历史登录列表(“喂!我今天上午3:00没有登录”)。
 

Drupal版本:

提供用户信息类别

老葛的Drupal培训班 Think in Drupal

如果你在http://drupal.org拥有一个帐号,首先登录并点击“我的帐号”链接,接着选择编辑标签,你就可以看到提供关于用户信息的类别的效果了。除了编辑你的帐号信息,比如你的密码以外,在其它类别中你还可以提供你的其它个人信息。在编写此书时,http://drupal.org支持编辑CVS信息、Drupal相关信息、个人信息、工作信息、以及接收新闻通讯的优选。
 
通过使用profile.module或者user钩子的categories操作,你可以添加像这些类别一样的信息类别;参看profile.module中的实现。
 

Drupal版本:

外部登录

 

有时候,你可能不想使用Drupal的本地users表。例如,可能你在另一个数据库中或者LDAP中,已经有了一个users表。在Drupal中,可以很方便的将外部认证集成到登录流程中来。

老葛的Drupal培训班 Think in Drupal

Drupal版本:

简单的外部认证(1)

老葛的Drupal培训班 Think in Drupal

让我们实现一个非常简单的外部认证模块,来说明外部认证的工作原理。假定你的公司只雇用名为Dave的员工,而基于名字和姓氏来分配用户名。对于任何以dave开头的用户名都将通过该模块的认证,所以用户davebrown, davesmith, 和davejones都将能够成功的登录。我们在这里,将会使用form_alter()来修改用户登录验证处理器,这样就可以调用我们自己的验证处理器了。下面是sites/all/modules/custom/authdave/authdave.info文件:
 
; $Id$
name = Authenticate Daves
description = External authentication for all Daves.
package = Pro Drupal Development
core = 6.x

Drupal版本:

简单的外部认证(2)

老葛的Drupal培训班 Think in Drupal

而下面则是实际的authdave.module:
 
<?php
// $Id$
 
/**
 * Implementation of hook_form_alter().
 * We replace the local login validation handler with our own.
 */
function authdave_form_alter(&$form, $form_state, $form_id) {
    // In this simple example we authenticate on username only,
    // so password is not a required field. But we leave it in
    // in case another module needs it.
    if ($form_id == 'user_login' || $form_id == 'user_login_block') {
        $form['pass']['#required'] = FALSE;
       
        // If the user login form is being submitted, add our validation handler.
        if (isset($form_state['post']['name'])) {
            // Find the local validation function's entry so we can replace it.
            $array_key = array_search('user_login_authenticate_validate',
                $form['#validate']);
       
            if ($array_key === FALSE) {
                // Could not find it. Some other module must have run form_alter().
                // We will simply add our validation just before the final                                  //validator.
                $final_validator = array_pop($form['#validate']);
                $form['#validate'][] = 'authdave_login_validate';
                $form['#validate'][] = $final_validator;
            }
            else {
                // Found the local validation function. Replace with ours.
                $form['#validate'][$array_key] = 'authdave_login_validate';
            }
        }
    }
}
 
/**
 * Form validation handler.
 */
function authdave_login_validate($form, &$form_state) {
    global $user;
    if (!empty($user->uid)) {
        // Another module has already handled authentication.
        return;
    }
    // Call our custom authentication function.
    if (!authdave_authenticate($form_state['values'])) {
        // Authentication failed; username did not begin with 'dave'.
        form_set_error('name', t('Unrecognized username.'));
    }
}
 
/**
 * Custom authentication function. This could be much more complicated,
 * checking an external database, LDAP, etc.
 */
function authdave_authenticate($form_values) {
    global $authdave_authenticated;
    $username = $form_values['name'];
    if (substr(drupal_strtolower($username), 0, 4) == 'dave') {
        // Log user in, or register new user if not already present.
        user_external_login_register($username, 'authdave');
       
        // Write session, update timestamp, run user 'login' hook.
        user_authenticate_finalize($form_state['values']);
        // Use a global variable to save the fact that we did authentication.
        // (See use of this global in hook_user() implementation of next
        // code listing.)
        $authdave_authenticated = TRUE;
        return TRUE;
    }
    else {
        // Not a Dave.
        return FALSE;
    }
}
 
    图6-4给出了Drupal的本地登录流程。它包含了3个表单验证器:
 
• user_login_name_validate():如果用户名被封,或者访问规则(Administer >> User management >> Access rules)拒绝了该用户名或主机的访问,那么就设置一个表单错误信息。
 
• user_login_authenticate_validate():使用此用户名,密码,以及状态设置为1(也就是,没有被封的),来对users表进行查询,如果查询失败,则设置一个表单错误信息。
 

user_login_final_validate():如果没有成功的加载用户,那么设置一个错误信息“对不起,无效的用户名或密码。你是不是忘记密码了?”,并向系统写入一个日志记录“用户尝试登录失败”。

Drupal版本:

简单的外部认证(3)

老葛的Drupal培训班 Think in Drupal

聪明的读者将会注意到,如果同时启用了Drupal的本地认证和我们的外部认证,那么在插入操作下面,没有方式通过代码来区分用户究竟是通过本地认证的还是通过外部认证的;所以我们在这里聪明的使用了一个全局变量,来指示是我们的模块进行了认证。我们还对authmap表进行了查询,如下所示:
 
db_query("SELECT uid FROM {authmap} WHERE uid = %d AND module = '%s'", $account->uid, 'authdave');
 
    所有通过外部认证添加的用户,同时会在users表和authmap表中各有一行记录。然而,在这种情况下,由于在同一请求期间运行了认证和user钩子,所以在这里使用一个全局变量就可以很好的替代一个数据库查询了。
 
6-7. 外部用户登录/注册流程的详细

Drupal版本:

总结

老葛的Drupal培训班 Think in Drupal

读完本章后,你应该能够
• 理解用户在Drupal内部是如何表示的
• 理解如何使用不同的方式来存储与用户相关的信息
• 使用用户注册过程中的钩子,来获取一个正在注册的用户的更多信息。
• 使用用户登录过程中的钩子,在用户登录时调用你自己的代码
• 理解外部用户认证的工作原理
• 实现你自己的外部认证模块
 
    更多关于外部认证的信息,可参看openid.module(Drupal核心可选模块), 或者第3方模块pubcookie.module。
 
 
译者注:
profile,在这章沿用了简体中文包中“个人资料”的译法,在更多的地方,我使用了“轮廓”的译法。
 

Drupal版本: