共计 5799 个字符,预计需要花费 15 分钟才能阅读完成。
关于本文
Typecho 是一款很好的博客程序,虽然相对 WordPress 来说功能略显单一,但在日常使用中来说还是很够用的了,使用 Typecho 提供的各种接口、功能等,咱们还是可以完成很多事情的,就比如自定义登录、注册页面!
由于 Typecho 的接口并不如 WordPress 一般强大,想要对 Typecho 的一些核心功能进行操作是比较麻烦的,如果按照一般的常理来说,要完成登录、注册页面的自定义功能,那肯定得修改核心代码了,但这样又会造成后期更新的不便。
所以,如果能够在不更改核心代码的同时完成咱们想要实现的功能,就有了这一篇文章!
实现原理
要实现自定义的登录、注册页面,其实原理很简单,就是对原有的登录、注册页面进行“替换”!
看到这里的朋友千万别打我,慢慢看下去,康康这里所说的替换并不是说直接替换文件,而是通过一系列操作,在保留原有系统核心代码不变的情况下进行页面替换的!
而康康所谓的一系列操作,其实说开了非常简单,自己写两个主题自定义页面扔主题目录里,然后在后台添加独立页面,而这两个页面就是自己编写的登录、注册功能页面,最后把相关的所有登录、注册链接替换为这两个页面的链接就 OK 了!
Typecho 流程分析
操作之前,咱们先对 Typecho 整个的登录、注册流程进行一下小小的分析,这样就方便接下来的编写、替换工作。
首先咱们知道,Typecho 的登录文件是在 admin 下的 login.php,注册文件是 admin 下的 register.php。
首先看看登录文件,其实就是一个简单的表单,提交了三个参数:name、password、remember
<form action="<?php $options->loginAction(); ?>" method="post" name="login" role="form">
<p>
<label for="name" class="sr-only"><?php _e('用户名'); ?></label>
<input type="text" id="name" name="name" value="<?php echo $rememberName; ?>" placeholder="<?php _e(' 用户名 '); ?>" class="text-l w-100" autofocus />
</p>
<p>
<label for="password" class="sr-only"><?php _e('密码'); ?></label>
<input type="password" id="password" name="password" class="text-l w-100" placeholder="<?php _e(' 密码 '); ?>" />
</p>
<p class="submit">
<button type="submit" class="btn btn-l w-100 primary"><?php _e('登录'); ?></button>
<input type="hidden" name="referer" value="<?php echo htmlspecialchars($request->get('referer')); ?>" />
</p>
<p>
<label for="remember"><input type="checkbox" name="remember" class="checkbox" value="1" id="remember" /> <?php _e('下次自动登录'); ?></label>
</p>
</form>
而提交的地址就是一个登录的操作动作,这个动作在哪里呢?var/Widget/Login.php
,就在这里,咱们打开看看!
文件中就只有一个方法action
,在其中对登录所提交的参数进行验证,最主要的就是其中的两行代
// 验证表单
$this->security->protect();
........ 省略
// 登录验证
$valid = $this->user->login($this->request->name, $this->request->password, false, 1 == $this->request->remember ? $this->options->time + $this->options->timezone + 30*24*3600 : 0);
再看看注册的流程,页面同样的是一个表单,不过不包含密码输入,这里就比贴代码了,咱们直接看看操作动作,ver/Widget/Register.php
,同样的,比较重要的代码也不多,这里只列出这些代码,其他省略
// 验证表单
$this->security->protect();
// 很关键,Typecho 的密码格式就靠这个了
$hasher = new PasswordHash(8, true);
// 原版是直接使用随机密码
$generatedPassword = Typecho_Common::randString(7);
// 这个就是用户的一个结构体
$dataStruct = array(
'name' => $this->request->name,
'mail' => $this->request->mail,
'screenName'=> $this->request->name,
'password' => $hasher->HashPassword($generatedPassword),
'created' => $this->options->time,
'group' => 'subscriber'
);
// 插入用户数据
$insertId = $this->insert($dataStruct);
OK,明白了基本的流程,咱们编写起来就方便多了!
登录界面编写
登录界面咱们需要的就是一个表单,最少提交两个参数:name 和 password,至于 remember 则是记住我的功能,需不需要自己决定。
<form action=""method="post">
<input type="hidden" name="_" value="<?php echo $this->security->getToken($this->request->getRequestUrl());?>">
用户名:<input type="text" name="name">
密码:<input type="password" name="password">
<input type="submit" value="提交">
</form>
这里只是一个最基本的表单功能,要如何美化则靠自己啦,这里康康只是进行最基本的演示而已,其中 _
这个参数是表单安全验证所用。
表单写好了,那提交到哪里呢?这里要看自己的需求,因为原版的登录过后,如果没有 referer 参数,则会跳转到后台,若您更改了后台路径,并且不想让别人知道,那就只能自己编写一个登录动作,然后提交到这里,如果不在意,则直接提交到原版登录动作就行!
这里康康就当作要保护后台路径,那么直接在这个文件中编写代码
// 已登录或不开启注册则跳转首页
if ($this->user->hasLogin() ) $this->response->redirect($this->options->siteUrl);
// 如果有请求
if ($this->request->isPost() ) {
// 验证表单
$this->security->protect();
// 如果已经登录
if ($this->user->hasLogin() ) $this->response->redirect($this->options->siteUrl);
// 初始化验证类
$validator = new Typecho_Validate();
$validator->addRule('name', 'required', _t('请输入用户名'));
$validator->addRule('password', 'required', _t('请输入密码'));
// 验证失败
if ($error = $validator->run($this->request->from('name', 'password')) )
$this->response->redirect($this->options->siteUrl);
// 验证用户
$valid = $this->user->login($this->request->name, $this->request->password, false);
// 验证成功
if ($valid) $this->response->redirect($this->options->siteUrl);
// 防止穷举, 休眠 3 秒
sleep(3);
// 失败跳转
$this->response->redirect($this->options->siteUrl);
}
其实就是把官方的动作几乎照抄了一遍,这样就能实现登录的功能,而且还能自己控制最终跳转的地址。
而注册页面也同样,建立一个表单,包含几个参数:name、mail、password、confirm,官方不支持自己输入密码,这里把 password 和 confirm 加入就能实现自定义密码了!
同样的,如果需要编写动作代码,则把官方的 copy 一份,不过其中有一点点小改动。
if ($this->request->isPost() ) {
// 表单验证
$this->security->protect();
// 如果已登录或未开启注册
if ($this->user->hasLogin() || !$this->options->allowRegister ) $this->response->redirect($this->options->siteUrl);
// 初始化验证类
$validator = new Typecho_Validate();
// 加上这行, 初始化一个 Users
$user = $this->widget('Widget_Abstract_Users');
$validator->addRule('name', 'required', _t('必须填写用户名称'));
$validator->addRule('name', 'minLength', _t('用户名至少包含 2 个字符'), 2);
$validator->addRule('name', 'maxLength', _t('用户名最多包含 32 个字符'), 32);
$validator->addRule('name', 'xssCheck', _t('请不要在用户名中使用特殊字符'));
// 这里有改动, 使用 $this 是无法调用的
$validator->addRule('name', array($user, 'nameExists'), _t('用户名已经存在'));
$validator->addRule('mail', 'required', _t('必须填写电子邮箱'));
// 这里同上
$validator->addRule('mail', array($user, 'mailExists'), _t('电子邮箱地址已经存在'));
$validator->addRule('mail', 'email', _t('电子邮箱格式错误'));
$validator->addRule('mail', 'maxLength', _t('电子邮箱最多包含 200 个字符'), 200);
$validator->addRule('password', 'required', _t('必须填写密码'));
$validator->addRule('password', 'minLength', _t('为了保证账户安全, 请输入至少八位的密码'), 8);
$validator->addRule('password', 'maxLength', _t('为了便于记忆, 密码长度请不要超过十八位'), 18);
$validator->addRule('confirm', 'confirm', _t('两次输入的密码不一致'), 'password');
// 验证失败
if ($error = $validator->run($this->request->from('name', 'password', 'mail', 'confirm')) )
$this->response->redirect($this->options->siteUrl);
$hasher = new PasswordHash(8, true);
// 由于是自定义密码, 所以这行可以注释了
// $generatedPassword = Typecho_Common::randString(7);
$dataStruct = array(
'name' => $this->request->name,
'mail' => $this->request->mail,
'screenName' => $this->request->name,
// 直接获取提交的密码并加密
'password' => $hasher->HashPassword($this->request->password),
'created' => $this->options->time,
'group' => 'subscriber'
);
// 初始化一个 Register
$register = $this->widget('Widget_Register');
// 插入用户数据
$insertId = $register->insert($dataStruct);
// 压入堆栈
$register->db->fetchRow($register->select()->where('uid = ?', $insertId)->limit(1), array($register, 'push'));
// 登录
$this->user->login($this->request->name, $this->request->password);
$this->response->redirect($this->options->siteUrl);
}
好了,代码写完了,把这两个文件扔到主题目录里,然后后台新建两个独立页面,再把登录、注册的链接更改为这两个独立页面地址,试试看是不是可以了?