作者: Administrator
2009-05-25
本文主要讨论Joomla中的用户登陆以及session注册流程与机制。Joomla的登陆过程通过组件com_user,
插件plugin/authenication/joomla.php、plugin/user/joomla.php共同完成,当然底层还
包括application/factory.php、joomla/session/session.php、joomla/session/storage等等诸多部
分共同完成。
首先讲讲登陆的整个过程,用户组件在接受用户登陆请求之后开始发起登陆认证流程autheni
cation过程其实就是向插件plugin/authenication/joomla.php发起认证请求。
function login()
{
// Check for request forgeries
JRequest::checkToken('request') or jexit( 'Invalid Token' );
global $mainframe;
if ($return = JRequest::getVar('return', '', 'method', 'base64')) {
$return = base64_decode($return);
if (!JURI::isInternal($return)) {
$return = '';
}
}
$options = array();
$options['remember'] = JRequest::getBool('remember', false);
$options['return'] = $return;
$credentials = array();
$credentials['username'] = JRequest::getVar('username', '', 'method', 'username');
$credentials['password'] = JRequest::getString('passwd', '', 'post', JREQUEST_ALLOWRAW);
//preform the login action
$error = $mainframe->login($credentials, $options);
if(!JError::isError($error))
{
// Redirect if the return url is not registration or login
if ( ! $return ) {
$return = 'index.php?option=com_user';
}
$mainframe->redirect( $return );
}
else
{
// Facilitate third party login forms
if ( ! $return ) {
$return = 'index.php?option=com_user&view=login';
}
// Redirect to a login form
$mainframe->redirect( $return );
}
}
上面就是登陆组件中的登陆请求部分,重点就是$options以及$credentials,前者记录了一些客户端的行为比如记住我 (remembe me),返回地址,后者类似于一个身份请求令牌,令牌中包括了用户输入的用户名以及密码,这些是要发送给主框架($mainframe)认证的
$mainframe->login($credentials, $options) 发送主框架认证用户信息是否合法
发送给主框架认证用户信息是如何进行的,我们继续深入代码中。
在libraries/joomla/application/application.php中可以看到如下代码:
jimport( 'joomla.user.authentication');
$authenticate = & JAuthentication::getInstance();
$response = $authenticate->authenticate($credentials, $options);
$authenicate->authenticate这个方法就负责认证请求登陆的用户信息,
$response是认证返回信息,很明了。但这一切还不是我想看到的最底层,继续向authenticate函数内部进军,发现了这个函数的全貌:
function authenticate($credentials, $options)
{
// Initialize variables
$auth = false;
// Get plugins
$plugins = JPluginHelper::getPlugin('authentication');
// Create authencication response
$response = new JAuthenticationResponse();
/*
* Loop through the plugins and check of the creditials can be used to authenticate
* the user
*
* Any errors raised in the plugin should be returned via the JAuthenticationResponse
* and handled appropriately.
*/
foreach ($plugins as $plugin)
{
$className = 'plg'.$plugin->type.$plugin->name;
if (class_exists( $className )) {
$plugin = new $className($this, (array)$plugin);
}
// Try to authenticate
$plugin->onAuthenticate($credentials, $options, $response);
// If authentication is successfull break out of the loop
if($response->status === JAUTHENTICATE_STATUS_SUCCESS)
{
if (empty( $response->type )) {
$response->type = isset( $plugin->_name ) ? $plugin->_name : $plugin->name;
}
if (empty( $response->username )) {
$response->username = $credentials['username'];
}
if (empty( $response->fullname )) {
$response->fullname = $credentials['username'];
}
if (empty( $response->password )) {
$response->password = $credentials['password'];
}
break;
}
}
return $response;
}
}
这下已经非常清晰了:
$plugins = JPluginHelper::getPlugin('authentication');
调用所有的authentication类的插件,然后循环认证之前的那个类似令牌的用户输入信息。
foreach ($plugins as $plugin)
{}
换句话说,Joomla可以接受多种认证方式同时进行用户认证,只要有一种认证通过就算通过认证,为什么?请看上文代码,注意那个break;
最后我们看看joomla的认证插件,对于如何用到认证插件进行用户认证的上文已经讲的很清楚了。现在一窥认证的究竟:
plugin/authentication/joomla.php
function onAuthenticate( $credentials, $options, &$response )
{
jimport('joomla.user.helper');
if($options['group']==''){
return false;
}
// Joomla does not like blank passwords
if (empty($credentials['password']))
{
$response->status = JAUTHENTICATE_STATUS_FAILURE;
$response->error_message = 'Empty password not allowed';
return false;
}
// Initialize variables
$conditions = '';
// Get a database object
$db =& JFactory::getDBO();
$query = 'SELECT `id`, `password`, `gid`'
. ' FROM `#__users`'
. ' WHERE username=' . $db->Quote( $credentials['username'] )
;
$db->setQuery( $query );
$result = $db->loadObject();
if($result)
{
$parts = explode( ':', $result->password );
$crypt = $parts[0];
$salt = @$parts[1];
$testcrypt = JUserHelper::getCryptedPassword($credentials['password'], $salt);
if ($crypt == $testcrypt) {
$user = JUser::getInstance($result->id); // Bring this in line with the rest of the system
$response->email = $user->email;
$response->fullname = $user->name;
$response->status = JAUTHENTICATE_STATUS_SUCCESS;
$response->error_message = '';
} else {
$response->status = JAUTHENTICATE_STATUS_FAILURE;
$response->error_message = 'Invalid password';
}
}
else
{
$response->status = JAUTHENTICATE_STATUS_FAILURE;
$response->error_message = 'User does not exist';
}
}
这里的认证非常明白了,根据用户输入的用户名查出数据库里对应的密码与用户密码匹配,一致就成功,不匹配就认证失败。密码加密方式可参见getCryptedPassword函数提供多种加密方式。
到这里登陆认证的流程基本分析了一遍,那么肯定有人问session在哪里注册了,怎么没见session 的踪迹?让
我们回溯代码到libraries/joomla/application/application.php这个文件,里面有一句异常重要的话:
// Import the user plugin group
JPluginHelper::importPlugin('user');
// OK, the credentials are authenticated. Lets fire the onLogin event
$results = $this->triggerEvent('onLoginUser', array((array)$response, $options));
这里其实trigger了一个事件 onLoginUser,意思是在用户登陆的时候要调用插件提前完成一些操作,session的操作就在这里。插件调用的机制用了PHP里的一个非常出名的设计模式,之前我有介绍《观察者设计模式》查看之前的文章:http://www.joomlar.cn/website/content/view /189/27/
关于session的底层操作下篇文章再详细介绍,joomla提供了两种session存储机制,利用的就是覆写PHP的session类来提供基于数据库以及文件的session存储机制。