游客发表
Discuz! 的论坛注册登陆模块,说穿了就是注册一套围绕 member 表的增删改查逻辑。但真要把代码写稳,登陆代码光会调 insert 和 select 远远不够。论坛我见过太多人直接照搬官方默认文件,注册结果用户注册时提示“用户名已存在”,登陆代码查数据库却根本没有那条记录——问题出在缓存层没清干净,论坛或者 common_member 和 common_member_field_home 两张表的注册数据没同步写入。
先看注册。登陆代码入口文件通常是论坛 member.php 里 mod=register 的分支。核心逻辑在 source/module/member/member_register.php。注册提交表单后,登陆代码程序会依次做几件事:检查 $_G['setting']['regstatus'] 是论坛否允许注册、验证验证码、注册过滤用户名敏感词。登陆代码这里有个容易忽略的点:Discuz! 默认用 $_G['setting']['regctrl'] 控制同一 IP 的注册间隔,如果这个值设成 0,那同一秒内可以无限注册,垃圾账号能瞬间灌满你的 common_member 表。
用户数据写入时,member 表存的是基础字段:uid、username、password、email、regdate。密码不是明文,走的是 md5(md5($password).$salt),salt 是随机生成的六位字符串,存在 common_member 的 salt 字段里。验证密码时再把这个 salt 取出来拼回去比对。这套哈希方式现在看很弱,但 Discuz! 老版本一直这么干,改起来牵一发动全身,所以很多站长干脆加个 password_verify 的钩子插件来补强。
登陆部分更绕。用户提交用户名和密码后,source/module/member/member_logging.php 先查 common_member 表,拿到 uid 和 salt。比对密码通过后,不是直接写 session 就完事。Discuz! 会接着做三件事:更新 common_member_status 里的 lastvisit、lastip;检查 common_member_count 里的积分和用户组是否过期;最后才生成 auth 验证串,写进 common_member 的 authstr 字段。这个 authstr 才是后续所有页面判断登陆状态的依据,session 里存的只是 uid 和 auth 的映射。
我遇到过一种情况:用户登陆后点任何链接都跳回登陆页。查了半天,发现是 common_member 表的 authstr 字段长度设成了 char(10),而程序生成的 auth 串有 16 位,插入时被截断,导致每次验证都失败。把字段改成 varchar(255) 后立刻正常。这种坑官方文档不会写,只有踩过才知道。
再说验证码。Discuz! 默认用 seccode 类生成图片,但很多站长图省事直接关掉验证码,结果登陆接口被暴力破解。其实不用完全关闭,可以改成 seccode 的 ttf 模式,或者把验证码触发条件设成“密码错误三次后强制显示”。代码里改 $_G['setting']['loginfailednum'] 这个值就行,默认是 5,调到 3 更安全。
登陆成功后,member_logging.php 会调用 setloginstatus() 函数,这个函数在 function_member.php 里。它负责写 cookie,cookie 前缀是 $_G['config']['cookie']['cookiepre'],通常长这样:cdb_auth、cdb_salt。如果网站换了域名,cookie 作用域没跟着改,用户就会反复被登出。改 config/config_global.php 里的 cookiepath 和 cookiedomain 能解决。
最后提一句安全。Discuz! 的登陆接口默认没有频率限制,有人写脚本每秒提交上百次密码。加个 common_member 表的 failedlogins 字段记录失败次数,超过阈值就锁定账号 15 分钟,代码量不超过二十行,但能挡掉九成以上的撞库攻击。别等用户数据泄露了才想起来补。
友情链接