Typecho不改核心代码实现自定义登录、注册功能

3,167次阅读

共计 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);
}

好了,代码写完了,把这两个文件扔到主题目录里,然后后台新建两个独立页面,再把登录、注册的链接更改为这两个独立页面地址,试试看是不是可以了?

正文完
 
Blood.Cold
版权声明:本站原创文章,由 Blood.Cold 2019-04-30发表,共计5799字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。