1
|
<a href=
"http://ux.com/member.php?mod=logging&action=login&loginsubmit=yes&floatlogin=yes&inajax=1"
>http://ux.com/member.php?mod=logging&action=login&loginsubmit=yes&floatlogin=yes&inajax=1</a>
|
1
2
3
4
5
6
7
8
9
10
11
|
$mod
= !in_array(
$discuz
->
var
[
'mod'
],
$modarray
) ?
'logging'
:
$discuz
->
var
[
'mod'
];
//mod的值即是接下来加载的php页面
define(
'CURMODULE'
,
$mod
);
$modcachelist
=
array
(
'register'
=>
array
(
'modreasons'
,
'stamptypeid'
,
'fields_required'
,
'fields_optional'
,
'ipctrl'
));
$cachelist
=
array
();
if
(isset(
$modcachelist
[CURMODULE])) {
$cachelist
=
$modcachelist
[CURMODULE];
}
$discuz
->cachelist =
$cachelist
;
$discuz
->init();
runhooks();
require
DISCUZ_ROOT.
'./source/module/member/member_'
.
$mod
.
'.php'
;
//完成程序的包含操作
|
打开source/module/member/member_logging.php文件,是一个类,在类的前面可看到下面三句代码:
$ctl_obj
=
new
logging_ctl();
$method
=
'on_'
.
$_G
[
'gp_action'
];
// $_G['gp_action'] 等于action的值即 login
$ctl_obj
->
$method
();
//$ctl_obj->on_login();
|
在类中可找到login方法,在方法中,大约 56 行有下面一个判断语句:
if
(!submitcheck(
'loginsubmit'
, 1,
$seccodecheck
)) {
|
判断语句是当游客浏览时,submitcheck 函数的返回值是假,取反,为真。
当用户登录时,程序走的是else部分,在里面可看到下面五句代码:
}
else
{
$_G
[
'uid'
] =
$_G
[
'member'
][
'uid'
] = 0;
$_G
[
'username'
] =
$_G
[
'member'
][
'username'
] =
$_G
[
'member'
][
'password'
] =
''
;
//变量赋值
$result
= userlogin(
$_G
[
'gp_username'
],
$_G
[
'gp_password'
],
$_G
[
'gp_questionid'
],
$_G
[
'gp_answer'
],
$_G
[
'setting'
][
'autoidselect'
] ?
'auto'
:
$_G
[
'gp_loginfield'
]);
//从数据库查询用户数据,并返回相应的信息
if
(
$result
[
'status'
] > 0) {
//状态值大于 0 ,说明有此用户,可以登录
setloginstatus(
$result
[
'member'
],
$_G
[
'gp_cookietime'
] ? 2592000 : 0);
//设置登录状态,即是写 COOKIE 操作,COOKIE 中的数据即是 SESSION 中相应的数据,但此函数并不负责写 SESSION 的操作
|
我们来看一下 source/function/function_login.php中的 setloginstatus 函数,是普通的写 COOKIE 操作,不再具体讲解:
function
setloginstatus(
$member
,
$cookietime
) {
global
$_G
;
$_G
[
'uid'
] =
$member
[
'uid'
];
$_G
[
'username'
] =
$member
[
'username'
];
$_G
[
'adminid'
] =
$member
[
'adminid'
];
$_G
[
'groupid'
] =
$member
[
'groupid'
];
$_G
[
'formhash'
] = formhash();
$_G
[
'session'
][
'invisible'
] = getuserprofile(
'invisible'
);
$_G
[
'member'
] =
$member
;
$_G
[
'core'
]->session->isnew = 1;
dsetcookie(
'auth'
, authcode(
"{$member['password']}\t{$member['uid']}"
,
'ENCODE'
),
$cookietime
, 1, true);
//authcode加密
dsetcookie(
'loginuser'
);
dsetcookie(
'activationauth'
);
dsetcookie(
'pmnum'
);
}
|
到这里可以说是登录流程大部分已经走完,但是 COOKIE 不清除时,会一直存在于客户端,如果超时,程序中会在判断弃用此 COOKIE,并重新写入。
下面我们来看一下 DZX 中 SESSION 操作的类,在 source/class/calss_core.php 文件中:
程序中每次请求都会加载 SESSION ,这是由核心类 discuz_core 中的 _init_session 方法来执行的,此方法被置于 类的 init方法中,说明每次加载类,会自动将 SESSION 写入。
function
_init_session() {
$this
->session =
new
discuz_session();
//创建 SESSION 类
if
(
$this
->init_session) {
//从 COOKIE 中读取数据
$this
->session->init(
$this
->
var
[
'cookie'
][
'sid'
],
$this
->
var
[
'clientip'
],
$this
->
var
[
'uid'
]);
$this
->
var
[
'sid'
] =
$this
->session->sid;
$this
->
var
[
'session'
] =
$this
->session->
var
;
//判断 SID 是否相等,不等,说明是多个用户在同一主机上登录网站,需要重新写 COOKIE
if
(
$this
->
var
[
'sid'
] !=
$this
->
var
[
'cookie'
][
'sid'
]) {
dsetcookie(
'sid'
,
$this
->
var
[
'sid'
], 86400);
}
if
(
$this
->session->isnew) {
if
(ipbanned(
$this
->
var
[
'clientip'
])) {
$this
->session->set(
'groupid'
, 6);
}
}
if
(
$this
->session->get(
'groupid'
) == 6) {
$this
->
var
[
'member'
][
'groupid'
] = 6;
sysmessage(
'user_banned'
);
}
//UID 不为空,且需要更新 SESSION 或是 SESSION 超时,更改用户状态,需要用户重新登录
if
(
$this
->
var
[
'uid'
] && (
$this
->session->isnew || (
$this
->session->get(
'lastactivity'
) + 600) < TIMESTAMP)) {
$this
->session->set(
'lastactivity'
, TIMESTAMP);
$update
=
array
(
'lastip'
=>
$this
->
var
[
'clientip'
],
'lastactivity'
=> TIMESTAMP);
if
(
$this
->session->isnew) {
$update
[
'lastvisit'
] = TIMESTAMP;
}
DB::update(
'common_member_status'
,
$update
,
"uid='"
.
$this
->
var
['uid
']."'
");
}
}
}
|
操作 SESSION 的类是 discuz_session ,我们看这个类里面的两个方法:
//此函数负责产生新的 SESSION,但并不负责写入数据库
function
create(
$ip
,
$uid
) {
//创建SESSION,执行插入数据,由随机函数产生一个六位随机数即是session的唯一值时间为当前时间,sid为cookie中的sid
$this
->isnew = true;
$this
->
var
=
$this
->newguest;
$this
->set(
'sid'
, random(6));
$this
->set(
'uid'
,
$uid
);
$this
->set(
'ip'
,
$ip
);
$this
->set(
'lastactivity'
, time());
$this
->sid =
$this
->
var
[
'sid'
];
return
$this
->
var
;
}
//此函数负责更新 SESSION
function
update() {
if
(
$this
->sid !== null) {
$data
= daddslashes(
$this
->
var
);
if
(
$this
->isnew) {
$this
->
delete
();
DB::insert(
'common_session'
,
$data
, false, false, true);
}
else
{
DB::update(
'common_session'
,
$data
,
"sid='$data[sid]'"
);
}
dsetcookie(
'sid'
,
$this
->sid, 86400);
}
}
|
至此我们知道了 SESSION 插入数据库的具体函数,与 COOKIE 的联系,但还不清楚是如何触发此操作的。
打开 source/function/function_core.php 文件,找到函数,updatesession ,此函数负责更新 SESSION :
function
updatesession(
$force
= false) {
global
$_G
;
static
$updated
= false;
if
(!
$updated
) {
$discuz
= & discuz_core::instance();
foreach
(
$discuz
->session->
var
as
$k
=>
$v
) {
if
(isset(
$_G
[
'member'
][
$k
]) &&
$k
!=
'lastactivity'
) {
$discuz
->session->set(
$k
,
$_G
[
'member'
][
$k
]);
}
}
foreach
(
$_G
[
'action'
]
as
$k
=>
$v
) {
$discuz
->session->set(
$k
,
$v
);
}
$discuz
->session->update();
$updated
= true;
}
return
$updated
;
}
|
我们在程序源码中搜索此函数,可以看到在很多的模板中都有下面一句代码:
{
eval
updatesession();}
|
浏览页面时将触发此函数,并将 SESSION 写入数据库。
整理一下思绪:
第一步:用户登录,程序将 COOKIE 写入客户端,这些 COOKIE 即是 SESSION 的部分数据,如SID、IP、TIME,不包含用户名、密码等关键信息。
第二步,登录成功后,程序会自动刷新页面,向服务器再次发送请求,服务器加载 discuz_core 核心类,并从 COOKIE 中读取到 SESSION 的相关信息,但还没有写入数据库。
第三步,核心类加载完成,程序继续执行,最后加载模板,触发 updatesession 函数,SESSION 被写入数据库。