WordPress 很多插件能够实现私有化,但都是基于默认的 Apache 服务器进行的配置,自动修改 .htaccess
的配置,从插件看配置生效的,实际上如果用的是其他服务器则没有任何用途,本文基于 Nginx 进行私有化站点搭建说明,包括nginx.conf
文件配置、插件推荐、function.php
文件配置等
先搭建公开站点
参考官方文档,进行基本配置即可
其中nginx可参考的核心配置:
server {
listen 14000 ssl default_server;
listen [::]:14000 ssl default_server;
server_name _;
cert.conf*;
ssl_prefer_server_ciphers on;
root "/xxxxxxx/wordpress";
index index.html index.htm index.cgi index.php index.php5 ;
location ~* \.(php[345]?|phtml)$ {
fastcgi_pass unix:/run/xxxx/php.sock;
fastcgi_connect_timeout 60s;
fastcgi_read_timeout 30s;
fastcgi_send_timeout 30s;
include fastcgi.conf;
}
# 引入自定义配置 -- 后面的所有都只修改这个自定义文件
include /usr/xxxx/customize/user.conf*;
}
插件推荐
安全与用户管理
- All In One WP Security:安全工具,可以限制注册,建议私有站点限制一下,所有注册账号都需要审核,而不是给一个最低权限的角色,毕竟登录进来了就可以做很多事情了。
Members:权限管理工具,这里可以配置开启私有站点限制用户必须登录,同时也可以创建自定义权限(可用于自定义代码片段里面针对特定文章类目的权限管理,这个需要再另一个插件编写)
Akismet Anti-Spam: Spam Protection:评论安全插件,可以过滤识别垃圾消息等,根据站点特征决定是否启用,如果是陌生人邀约制建议使用,不排除有人账号被盗等情况。
WP Mail SMTP:邮件SMTP工具,根据注册机制决定是否使用,如果允许自行注册需要安装这个插件给注册人发邮件,如果是管理员主动创建账号则可以省掉。
编辑器相关
经典编辑器 + The Paste:不喜欢古登堡(块编辑器)的,可以通过这个插件使用以前的编辑方式。The Paste 是为了实现图片粘贴自动上传插入到文章的工具,省的每张图片费劲的上传、插入。
WP Editor.md:Markdown 语言编辑器,同时支持mermaid.js工具进行流程图、时序图绘制等。
Table of Contents Plus:目录自动生成工具,如果站点文章特点都是长篇文章可以使用此插件自动生成目录。
Mammoth .docx converter:支持 word 文档直接上传识别成文章的工具。
Shortcodes Ultimate:简码,很方便的工具,可以在文章中插入很多现成的组件,让内容更有组织层次感,根据站点文章特征选择是否需要此功能。
MarkText:这不是插件,一个独立的跨平台app,可以在本地进行MarkDown文档撰写,可与上面WP Editor配合,可替代已经成为收费软件的Typora,相比较VS code更轻量级。
其他工具
- WPForms Lite:表单工具,如果想收集意见反馈 or 部分人可见的投票等功能,都可以使用这个表单。
- Code Snippets:自定义代码片段。不建议直接修改functions.php文件,建议在这个插件里面操作,如果改错了网站挂了,也可以快速的去数据库找到此插件的表进行调整,原则上Wordpress官方框架不做任何改动。
- WP Super Cache:按需使用,私有站点可能每个人看到的内容不一样,缓存不一定好。
functions.php 代码调整
建议使用上面提到的 Code Snippets 插件进行框架代码增补,而不是直接修改文件。
注册登录页面logo调整
私有站点免不了登录操作,对于新用户来说首次访问网站看到的大概率就是这个页面,这是让自己的网站的logo深入人心的时刻,建议改一下。
注意 把logo 放到当前选中主题的根目录下,因为
get_bloginfo('template_directory')
获取的是当前主题的根目录,如果需要其他路径,请特别调整。特别注意,如果放到了媒体库,请注意是否媒体库已经私有化了,如果按照后文的调整把媒体库私有化了非登录无法查看,此时图片不能放到媒体库,因为这个登录页面用户还没登陆(当然也可以在规则里面特别调整一下把这个logo文件设定为免登录查阅)。
<?php
//登录注册页面logo删除
function coologic_custom_loginlogo() {
echo '<style type="text/css">
h1 a {background-image: url('.get_bloginfo('template_directory').'/coologic_logo.png) !important; }
</style>';
}
add_action('login_head', 'coologic_custom_loginlogo');
//登录注册页面logo跳转连接改为当前站,也可以改成其他地址
function coologic_custom_login_url($url) {
return get_bloginfo('url');
}
add_filter( 'login_headerurl', 'coologic_custom_login_url');
删除管理后台左上角的logo
这个logo点击等都会跳转到 WordPress网站,如果是内部站点且内网情况,可能网站都打不开,此时可以删除这个logo。
<?php
//删除管理后台左上角的logo
function coologic_remove_admin_site_wordpress_logo() {
global $wp_admin_bar;
$wp_admin_bar -> remove_menu('wp-logo');
}
add_action('wp_before_admin_bar_render', 'coologic_remove_admin_site_wordpress_logo');
提效脚本-上传图片默认全尺寸展示
如果是内部站点,内网访问,带宽任性,此时可以改成默认全尺寸,省的使用经典编辑器的时候默认中尺寸,有些图会模糊,还需要人工调整,影响文章撰写效率。
<?php
function coologic_image_size_names_choose($image_sizes){
unset($image_sizes['medium']);
unset($image_sizes['large']);
unset($image_sizes['thumbnail']);
return $image_sizes;
}
add_filter('image_size_names_choose', 'coologic_image_size_names_choose');
## 复杂权限控制
使用Member
插件可以新建角色、给角色赋权,但是 wp 只实现了默认的权限,复杂的逻辑就需要定制权限了,可以用 Member
创建自定义权限,通过下述代码实现权限的用途。
理论上是可以针对用户id、针对角色直接进行控制,但仍然建议使用相对正规的自定义权限、角色赋权、实现自定义权限控制的方式进行全站权限管控。否则人员变动、角色增减等可能导致信息泄露,后台只能配置用户与角色的关系,不可能随着人员随时调整代码。
下面是一个针对自定义权限 coologic_read_all_special_category
权限的控制逻辑,目标是:只有拥有这个权限的角色,才能查看 16、17、18类目的文章,没有权限的人打开这类文章会直接跳转到网站首页。实现置顶类目文章对指定角色可见。
coologic_read_all_special_category 权限是在
Memeber
插件创建的,创建后还需要编辑各个角色,确定哪些角色有这个权限。
<?php
//这里换一个写法,直接把函数置入
add_action('template_redirect',function(){
//没有coologic_read_all_special_category权限的用户,禁止查看 16 17 18 类目的内容
//is_single 是否为文章
//类目必须穿类目id,16不是类目名称,id可以编辑类目,在url里找到
if(in_category(array( 16,17,18 )) && !current_user_can('coologic_read_all_patents_category') && !is_home() && is_single()) {
$url = home_url();
wp_safe_redirect($url);
exit();
}
});
Nginx 私有化配置调整
固定链接配置-伪静态
当前所有文章的链接都是 xxx/?id=xx 这类查询语句路径,如果想要改成一种固定格式,会发现网页会 404,因为 nginx 查不到文件位置,因此需要实现伪静态能力,Wordpress 自身已提供对应php文件,我们只需要把固定链接指向即可
特别注意 nginx 的注释标记是 # 号,别用 // 否则解析失败
# 引导所有链接,尝试直接访问文件如果不存在,就访问根目录的 /index.php 文件并附带参数
location / {
try_files $uri $uri/ /index.php?$args;
}
# 针对管理后台进行重写,避免伪静态以后管理页异常
rewrite /wp-admin$ $scheme://$host$uri/ permanent;
媒体库私有化
经过上述的操作,我们可以体验到,不登录是无法查看任何文章页面了。
但不要忽视一个特殊的地方:媒体库。可以尝试一下不登录的情况下是否可以直接访问、下载一个媒体文件?正茬是可以访问,此时如果想要全站私有化,需要限制媒体库文件访问必须是用户已登录状态。
这时候使用 functions.php
文件调整已经无法做到了,因为静态文件是服务器直接路由的,没有走到任何 php 代码,直接由 Nginx 查到了文件就返回了,因此我们需要配置在访问媒体库路径时,通过php文件进行处理
首先进行nginx 配置
location ~ ^/wp-content/uploads/(.*) {
# 把所有请求都带到这个指定php执行文件,若执行失败或异常都返回403,文件放在根目录即可
try_files /coologic_uploads_file_read.php =403;
root "/xxxxx/wordpress"; # 指向根目录
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; # 必须要有这个否则拿不到参数
include fastcgi_params;
# 这个值一定要和上面的核心配置值一样,不要用不同的sock,否则会可能导致网站错乱
fastcgi_pass unix:/run/xxxx/php.sock;
}
下面是 coologic_uploads_file_read.php 逻辑,其中特别注意要处理url里中文被编码的问题。如果自己的链接中对中文进行了更复杂的编码,请根据情况调整解码逻辑,确保能找到文件。
<?php
require_once('wp-load.php');
//判断是否登录,未登录则跳到登录页
If (!is_user_logged_in()){
$upload_dir = wp_upload_dir();
wp_redirect( wp_login_url( $upload_dir['baseurl'] . '/' . $_GET[ 'file' ]));
exit();
}
//获取目录路径地址
list($basedir) = array_values(array_intersect_key(wp_upload_dir(), array('basedir' => 1)))+array(NULL);
//开始获取文件名($_GET[ 'file' ] 是文件名,若未取到需要用$_SERVER['REQUEST_URI'] 但这样就是完整的路径了
$getFile = isset($_GET[ 'file' ]) ? $_GET[ 'file' ] : $_SERVER['REQUEST_URI'];
//如果是完整的路径就去掉前缀
$getFile = str_replace('..', '', $getFile);
$getFile = str_replace('wp-content/uploads/', '', $getFile);
//拼接处最终的文件地址
$file = trailingslashit($basedir) . $getFile;
//判断文件存在性
if (!$basedir || !is_file($file)) {
//尝试解码url查询,有的url存在中文的问题,中文文件名会被自动编码为 %xx%xx,但硬盘里文件名是中文,需要解码
if(is_file(urldecode($file))) {
//存在就修改为解析后的路径
$file = urldecode($file);
} else {
//仍然没找到返回404+内容,如果需要调试请使用注释的一行,但不建议长期使用,会透出自己的目录结构
status_header(404);
die('455 coologic File not found.');
//die('455 File not found. file_path:'.$file.'#######'.urldecode($file));
}
}
//构建返回结果
$mime = wp_check_filetype($file);
if( false === $mime[ 'type' ] && function_exists( 'mime_content_type' ) )
$mime[ 'type' ] = mime_content_type( $file );
if( $mime[ 'type' ] )
$mimetype = $mime[ 'type' ];
else
$mimetype = 'image/' . substr( $file, strrpos( $file, '.' ) + 1 );
header( 'Content-Type: ' . $mimetype );
//返回文件
readfile( $file );
可能还有其他遗漏,欢迎大家补充。
最新评论