抽空把站内资源都优化了一遍,包括 SSL 与重定向、多说与 SSL 相关问题、七牛云图床、Geetest 等。文章主要作日志使用,方便自己日后回顾这些问题,如果你所处环境为 Typecho 且有相关问题,可以详细阅读一下。

SSL

免费 SSL 可选择的范围不多,先是申请了 StartSSL 的一年免费证书,但发现其一年免费证书已被移除信任。接着找到SSL For Free这个 Let's Encrypt 第三方,操作步骤很详尽,验证主机提供了三种选择方案。同样的,Let's Encrypt 证书需要每三个月更新一次,如果你没有 Let's Encrypt 所需要的环境,那么可以借由此服务获得证书。

重定向

如果希望所有 http 访问重定向到 https,那么可以修改 Apache 或 Nginx 重写规则。关于 Apache,可以按以下规则修改.htaccess(Typecho 适用)

/* http 301 到 https */
RewriteCond %{SERVER_PORT} !^443$
RewriteRule ^(.*)$ https://www.yourdomain.com/$1 [L,R=301]

/* 一级域名定向到 www 二级域名 */
RewriteCond %{HTTP_HOST} ^yourdomain.com
RewriteRule ^(.*)$ www.yourdomain.com/$1 [L,R=301]

/* 部分资源不重定向(不完整片段) */
RewriteCond %{REQUEST_URI} !^/root/
RewriteCond %{REQUEST_URI} !/plugin/
RewriteRule ^(.*)$ https://www.yourdomain.com/$1 [L,R=301]

最终,若希望所有一级域名定向到 www 二级域名、http 301 到 https 且部分资源不重定向,可以把规则合并为:

RewriteEngine On
RewriteBase /
RewriteCond %{SERVER_PORT} !^443$ [OR]
RewriteCond %{HTTP_HOST} ^yourdomain.com [NC]
RewriteCond %{REQUEST_URI} !^/root/
RewriteCond %{REQUEST_URI} !/plugin/
RewriteRule ^(.*)$ https://www.yourdomain.com/$1 [L,R=301]

多说 SSL 兼容

全站 SSL 后,寻找多说评论框的替代品可谓一波三折。国外评论平台响应稍慢,国内又未能找到可以满足所有要求的平台——支持 HTTPS、支持自定义样式、支持评论后台回调。苦于种种高要求,只好继续沿用多说评论框。多说的功能其实比较完整,既支持无登录评论,又能够定义所有样式,可惜管理团队不上心,服务停滞不前。多说最让人诟病的是经常无法加载拖慢网页响应,且第三方登录头像仍未支持 HTTPS。对此,我们可以进一步优化。

  • 多说第三方头像兼容 HTTPS1

    1. 下载官方embed.js并格式化;
    2. 搜索关键字avatar并如图修改;多说第三方头像
    3. 把修改后的文件重新上传到云端(建议使用 CDN 服务);
    4. 修改多说评论模版地址(Typecho 适用)Typecho 多说评论模版修改
  • 多说资源异地保存,基本解决拖网速问题:

    1. 主要为embed.compat.js,其次为embed.default.css,同时还有smilies.js,分别下载三个文件;
    2. 把下载好的 embed.compat.js 重命名为libs/embed.compat.js,embed.default.css 重命名为styles/embed.default.css,smilies.js 重命名为 libs/smilies.js,分别上传到云端(建议使用 CDN 服务);
    3. 向 embed.js 搜索关键词STATIC_URL并如图修改;多说资源异地保存
    4. 保存 embed.js 方法同上。

七牛云图床

七牛云有免费的 HTTP 下载流量,但 HTTPS 仍要收费。如果希望图片及文件能够上传到七牛云的同时备份到本地服务器,可以利用七牛空间的镜像功能。

  • 直接使用插件SimpleCDN
  • 或搜索关键字content(,如图修改/var/Widget/Abstract/Contents.php文件(Typecho 适用)Typecho content() 函数修改

值得注意的是,修改本地文件不能影响七牛云镜像,如有修改请务必手动上传更改。

Geetest

Geetest for Typecho 可通过插件解决2

  1. 下载并启用插件Geetest
  2. 新增/admin/geetest-code.php文件;

    <?php
    if (!defined('__DIR__')) {
        define('__DIR__', dirname(__FILE__));
    }
    
    define('__TYPECHO_ADMIN__', true);
    
    /** 载入配置文件 */
    if (!defined('__TYPECHO_ROOT_DIR__') && !@include_once __DIR__ . '/../config.inc.php') {
        file_exists(__DIR__ . '/../install.php') ? header('Location: ../install.php') : print('Missing Config File');
        exit;
    }
    
    /** 初始化组件 */
    Typecho_Widget::widget('Widget_Init');
    
    /** 如果插件已启用, 则输出极验服务器响应数据 */
    /** 之前旧版本写法(已废弃)
    $exists = Typecho_Plugin::exists('Geetest');
    if(false !== $exists) {
        Typecho_Plugin::factory('gt')->server();
    }
    **/
    if(!empty(Helper::options()->plugins['activated']['Geetest'])) {
        Typecho_Plugin::factory('gt')->server();
    }
  3. 修改/admin/login.php代码(建议对比修改);

    <?php
    include 'common.php';
    
    if ($user->hasLogin()) {
        $response->redirect($options->adminUrl);
    }
    $rememberName = htmlspecialchars(Typecho_Cookie::get('__typecho_remember_name'));
    Typecho_Cookie::delete('__typecho_remember_name');
    
    $bodyClass = 'body-100';
    
    include 'header.php';
    ?>
    
    <div class="typecho-login-wrap">
        <div class="typecho-login">
            <h1><a href="http://typecho.org" class="i-logo">Typecho</a></h1>
            <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>
                <?php Typecho_Plugin::factory('gt')->render(); ?>
                <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>
            
            <p class="more-link">
                <a href="<?php $options->siteUrl(); ?>"><?php _e('返回首页'); ?></a>
                <?php if($options->allowRegister): ?>
                &bull;
                <a href="<?php $options->registerUrl(); ?>"><?php _e('用户注册'); ?></a>
                <?php endif; ?>
            </p>
        </div>
    </div>
    <?php 
    include 'common-js.php';
    /** 如果插件已启用, 则输出极验服务器响应数据 */
    $geePluginEnable = !empty(Helper::options()->plugins['activated']['Geetest']) ? true : false;
    ?>
    <script>
    $(document).ready(function () {
        $('#name').focus();
    
        <?php if($geePluginEnable):?>
        //极客验证码验证
        (function(){
            var handler = function (captchaObj) {
                // 将验证码加到id为captcha的元素里
                captchaObj.appendTo("#captcha");
            };
            $.ajax({
                // 获取id,challenge,success(是否启用failback)
                url: "geetest-code.php?rand="+Math.random()*100,
                type: "get",
                dataType: "json", // 使用jsonp格式
                success: function (data) {
                    // 使用initGeetest接口
                    // 参数1:配置参数,与创建Geetest实例时接受的参数一致
                    // 参数2:回调,回调的第一个参数验证码对象,之后可以使用它做appendTo之类的事件
                    initGeetest({
                        gt: data.gt,
                        challenge: data.challenge,
                        product: type, // 产品形式float-浮动式 embed-嵌入式
                        offline: !data.success //支持本地验证
                    }, handler);
                }
            });
        })();
        <?php endif;?>
    });
    </script>
  4. 修改/var/Widget/Login.php代码(建议对比修改);

    <?php
    if (!defined('__TYPECHO_ROOT_DIR__')) exit;
    
    class Widget_Login extends Widget_Abstract_Users implements Widget_Interface_Do
    {
        public function action()
        {
            // protect
            $this->security->protect();
    
            /** 如果已经登录 */
            if ($this->user->hasLogin()) {
                /** 直接返回 */
                $this->response->redirect($this->options->index);
            }
    
            /** 初始化验证类 */
            $validator = new Typecho_Validate();
            $validator->addRule('name', 'required', _t('请输入用户名'));
            $validator->addRule('password', 'required', _t('请输入密码'));
    
            /** 截获验证异常 */
            if ($error = $validator->run($this->request->from('name', 'password'))) {
                Typecho_Cookie::set('__typecho_remember_name', $this->request->name);
    
                /** 设置提示信息 */
                $this->widget('Widget_Notice')->set($error);
                $this->response->goBack();
            }
    
            /** 极客验证码校验 **/
            $this->verifyGeeTest();
    
            /** 开始验证用户 **/
            $valid = $this->user->login($this->request->name, $this->request->password,
            false, 1 == $this->request->remember ? $this->options->gmtTime + $this->options->timezone + 30*24*3600 : 0);
    
            /** 比对密码 */
            if (!$valid) {
                /** 防止穷举,休眠3秒 */
                sleep(3);
    
                $this->pluginHandle()->loginFail($this->user, $this->request->name,
                $this->request->password, 1 == $this->request->remember);
    
                Typecho_Cookie::set('__typecho_remember_name', $this->request->name);
                $this->widget('Widget_Notice')->set(_t('用户名或密码无效'), 'error');
                $this->response->goBack('?referer=' . urlencode($this->request->referer));
            }
    
            $this->pluginHandle()->loginSucceed($this->user, $this->request->name,
            $this->request->password, 1 == $this->request->remember);
    
            /** 跳转验证后地址 */
            if (NULL != $this->request->referer) {
                $this->response->redirect($this->request->referer);
            } else if (!$this->user->pass('contributor', true)) {
                /** 不允许普通用户直接跳转后台 */
                $this->response->redirect($this->options->profileUrl);
            } else {
                $this->response->redirect($this->options->adminUrl);
            }
        }
    
        private function verifyGeeTest()
        {
            $status = array(
                'empty'   => '请进行拼图验证',
                'failed'  => '验证失败',
                'success' => '验证通过',
                'down'    => '请求超时,请重试',
                'error'   => '服务器异常,请重试'
            );
            /** 如果插件已启用, 则进行验证 */
            if(!empty(Helper::options()->plugins['activated']['Geetest'])) {
                $data = $this->request->from('geetest_challenge', 'geetest_validate', 'geetest_seccode');
                $response = Typecho_Plugin::factory('gt')->verify($data);
                if($response == 'success') {
    
                } else {
                    $error  = !empty($status[$response]) ? $status[$response] : $status['error'];
                    $this->widget('Widget_Notice')->set($error);
                    $this->response->goBack();
                }
            }
        }
    }

值得注意的是,Geetest 访问 HTTPS 需要用户付费,使用时请自行斟酌。

声明

在此,再次感谢引用到的作者与资源。文章聚合这些资源主要为个人日后回顾使用,如有侵犯请及时告知。

  • Cacher提供的多说第三方头像 SSL 解决方案;
  • 没那么简单提供的 Geetest for Typecho 解决方案。

  1. 出自:多说完美https改造,是能够搜索到的最高效的方案。
  2. 出自:typecho极验验证插件

如有问题,欢迎留言或邮件咨询

  • « 上一篇:Workflow - Music Launcher