使用 Nginx + SSH 实现内网穿透

nginx

一、前言

什么是内网穿透?

内网穿透,也即 NAT 穿透,进行 NAT 穿透是为了使具有某一个特定源 IP 地址和源端口号的数据包不被 NAT 设备屏蔽而正确路由到内网主机。简而言之,就是公网可以直接访问我们本地电脑上的服务。

为什么需要内网穿透?

很多时候,我们需要公网可以直接访问本地电脑上的服务。对于开发人员来说,最常见的一种情况就是微信开发。微信的开发模式配置、登录授权回调、支付通知等功能都需要有公网可以直接访问的地址。对于初次对接的开发人员来说,需要大量的调试工作,如果通过上传代码的方式进行开发,不但浪费时间还不方便调试。

二、实现

实现内网穿透的方法有很多,今天介绍使用 Nginx proxy_pass 配合 SSH 隧道实现内网穿透。请先准备好一台服务器,并安装 Nginx。

服务器上进行如下操作

在 Nginx 的配置文件中添加如下配置,并重启服务:

server {

    listen       80;
    server_name dev.test.com;        // 改为你的域名即可

    location / {
        proxy_pass http://127.0.0.1:8080;  // 开放一个本地端口,用来转发上面定义的 80 端口的内容
        proxy_redirect off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Cookie $http_cookie;
    }

}

本地电脑进行如下操作

打开命令行并执行如下命令,回车后会提示输入服务器 root 账号的密码:

$ ssh -CNg -R 8080:127.0.0.1:8080 -o TCPKeepAlive=yes -o ServerAliveInterval=10 -o ServerAliveCountMax=2 -o ExitOnForwardFailure=yes root@dev.test.com

启动本地的 Web 服务,端口为 8080。在公网通过域名 http://dev.test.com/wechat/service 即可访问本地的 http://127.0.0.1:8080/wechat/service 服务。然后就可以愉快的在本地进行微信的开发调试了。

说明:8080:127.0.0.1:8080 中第一个 8080 是 proxy_pass 的端口,第二个 8080 是本地服务的端口,根据实际情况更改。

提示端口被占用

如果本地电脑断开后连不上,提示端口已占用,在服务器上运行如下命令:

$ lsof -i:8080

找到占用的进程,执行如下命令,kill 掉对应的进程,然后重新连接即可:

$ kill -9 进程ID

十分钟,带你了解 MySQL 主从原理,手把手教你使用 Docker 搭建 MySQL 主从复制

mysql-logo

一、前言

项目初期,很多都是单体架构,业务代码写在一起,数据表也都在一个数据库中;

随着项目的发展,访问量越来越大、数据量越来越多,数据库大概率会首先成为性能的瓶颈;这个时候一般都会升级项目架构为读写分离,以缓解主库的压力,提高系统的性能。MySQL 的主从复制是读写分离的前提。

二、原理

MySQL 主从复制流程图

可以看到,当主库接收到客户端的更新(insert/update/delete)请求后,执行内部事务的更新逻辑,同时写 binlog。

备库 slave 跟主库 master 之间维持了一个长连接。主库 master 内部有一个线程,专门用于服务备库 slave 的这个长连接。一个事务日志同步的完整过程是这样的:

  1. 在备库 slave 上通过 change master 命令,设置主库 master 的 IP、端口、用户名、密码,以及要从哪个位置开始请求 binlog,这个位置包含文件名和日志偏移量;
  2. 在备库 slave 上执行 start slave 命令,这时候备库会启动两个线程,就是图中的 io_thread 和 sql_thread。其中 io_thread 负责与主库建立连接;
  3. 主库 master 校验完用户名、密码后,开始按照备库 slave 传过来的位置,从本地读取 binlog,发给 slave;
  4. 备库 slave 拿到 binlog 后,写到本地文件,称为中转日志(relay log);
  5. sql_thread 读取中转日志,解析出日志里的命令,并执行。这里需要说明,后来由于多线程复制方案的引入,sql_thread 演化成为了多个线程。

三、实现

以下方法是基于 Docker 搭建的 MySQL 8.0 一主两从环境。容器名分别为 mysql-master、mysql-slave1、mysql-slave2。

创建 mysql master 服务

打开命令行,使用 docker 创建 mysql master 服务:

$ docker run -p 3300:3306 --name mysql-master -e MYSQL_ROOT_PASSWORD=123456 -d mysql:8.0

进入 docker 容器:

$ docker exec -it mysql-master /bin/bash

/etc/mysql/my.cnf 文件中 [mysqld] 下添加如下配置(需安装 vim,不会的拉到最后):

[mysqld]
# 同一局域网内唯一
server-id=3300
# binlog 文件名
log-bin=master-bin
# 同步的数据库
binlog-do-db=shop
# binlog 格式 statement、mixed、row,默认为 statement,议为 mixed、row
binlog_format=row

# 身份验证插件、和主从无关
default_authentication_plugin=mysql_native_password

重启 mysql 服务(退出并重启 docker 容器):

$ exit
$ docker restart mysql-master

# 非 docker 环境用如下命令重启即可
$ service mysql restart

进入 docker 容器,登录 mysql ,在 master 数据库创建一个用户 slave,授予 REPLICATION SLAVE 和 REPLICATION CLIENT 权限,用于在主从库之间同步数据:

$ docker exec -it mysql-master /bin/bash
$ mysql -uroot -p123456
# 创建一个账号
mysql> CREATE USER 'slave'@'%' IDENTIFIED BY '123456';
Query OK, 0 rows affected (0.00 sec)
# 授予这个账号权限
mysql> GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'slave'@'%';
Query OK, 0 rows affected (0.00 sec)
# 刷新权限
mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

通过命令行 show master status 查看当前 binlog 日志的信息,后面有用:

mysql> show master status;
+-------------------+----------+--------------+------------------+-------------------+
| File              | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+----------+--------------+------------------+-------------------+
| master-bin.000001 |      848 | shop         |                  |                   |
+-------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

创建 mysql slave1 服务

打开新的命令行,使用 docker 创建 mysql slave1 服务:

$ docker run -p 3301:3306 --name mysql-slave1 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:8.0

进入 docker 容器:

$ docker exec -it mysql-slave1 /bin/bash

/etc/mysql/my.cnf 文件中 [mysqld] 下添加如下配置:

[mysqld]
# 同一局域网内唯一
server-id=3301
# 从库只读 (1.只读 0.读写)
read_only=1     

# 身份验证插件、和主从无关
default_authentication_plugin=mysql_native_password

重启 mysql 服务(退出并重启 docker 容器):

$ exit
$ docker restart mysql-slave1

# 非 docker 环境用如下命令重启即可
$ service mysql restart

进入 docker 容器,登录 mysql ,设置主库信息:

master_host:master 服务的地址,使用容器独立的 ip ,用 docker inspect --format='{{.NetworkSettings.IPAddress}}' mysql-master 查询

$ docker exec -it mysql-slave1 /bin/bash
$ mysql -uroot -p123456

# 设置主库信息
mysql> change master to 
master_host='172.17.0.2',         -- master mysql 服务地址 
master_port=3306,                         -- master mysql 服务端口,使用容器的端口,即:3306
master_user='slave',                     -- master mysql 提供的账号
master_password='123456',         -- master mysql 提供的密码
master_log_file='master-bin.000001',     -- master mysql 服务 show master status 的 File 字段值
master_log_pos=848,                                     -- master mysql 服务 show master status 的 Position 字段值
master_connect_retry=30;

# 开启主从复制
mysql> start slave;        

show slave status 检测是否配置成功,Slave_IO_RunningSlave_SQL_Running 为 Yes 说明配置成功:

mysql> show slave status \G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 172.17.0.2
                  Master_User: slave
                  Master_Port: 3306
                Connect_Retry: 30
              Master_Log_File: master-bin.000001
          Read_Master_Log_Pos: 848
               Relay_Log_File: 5180d9b88f01-relay-bin.000002
                Relay_Log_Pos: 325
        Relay_Master_Log_File: master-bin.000001
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes

创建 mysql slave2 服务

再打开一个新的命令行,使用 docker 创建 mysql slave2 服务:

$ docker run -p 3302:3306 --name mysql-slave2 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:8.0

进入 docker 容器:

$ docker exec -it mysql-slave2 /bin/bash

/etc/mysql/my.cnf 文件中 [mysqld] 下添加如下配置:

[mysqld]
# 同一局域网内唯一
server-id=3302
# 从库只读 (1.只读 0.读写)
read_only=1     

# 身份验证插件、和主从无关
default_authentication_plugin=mysql_native_password

重启 mysql 服务(退出并重启 docker 容器):

$ exit
$ docker restart mysql-slave2

# 非 docker 环境用如下命令重启即可
$ service mysql restart

进入 docker 容器,登录 mysql ,设置主库信息:

$ docker exec -it mysql-slave2 /bin/bash
$ mysql -uroot -p123456

# 设置主库信息
mysql> change master to 
master_host='172.17.0.2',         -- master mysql 服务地址 
master_port=3306,                         -- master mysql 服务端口,使用容器的端口,即:3306
master_user='slave',                     -- master mysql 提供的账号
master_password='123456',         -- master mysql 提供的密码
master_log_file='master-bin.000001',     -- master mysql 服务 show master status 的 File 字段值
master_log_pos=848,                                     -- master mysql 服务 show master status 的 Position 字段值
master_connect_retry=30;

# 开启主从复制
mysql> start slave;        

show slave status 检测是否配置成功,Slave_IO_RunningSlave_SQL_Running 为 Yes 说明配置成功:

mysql> show slave status \G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 172.17.0.2
                  Master_User: slave
                  Master_Port: 3306
                Connect_Retry: 30
              Master_Log_File: master-bin.000001
          Read_Master_Log_Pos: 848
               Relay_Log_File: e34e0c0fcd86-relay-bin.000002
                Relay_Log_Pos: 325
        Relay_Master_Log_File: master-bin.000001
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes

创建 mysql slave N 服务

如需创建更多从库,只需更改 slave1 配置步骤中容器的端口、名称以及 server-id 即可。

测试主从复制

主从复制环境是否真正搭建成功,只需在 master 服务创建 shop 库,执行 insert/update/delete 语句,看从库是否同步这些操作即可,如果同步说明搭建成功。

四、杂记

安装 vim

$ apt-get update
$ apt-get install vim

本文首发于马燕龙个人博客,欢迎分享,转载请标明出处。
马燕龙个人博客:https://www.mayanlong.com
马燕龙个人微博:http://weibo.com/imayanlong
马燕龙Github主页:https://github.com/yanlongma

小程序系列 - 登录获取 UnionID 方式

一、UnionID 机制说明

如果开发者拥有多个移动应用、网站应用、和公众帐号(包括小程序),可通过 unionid 来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号(包括小程序),用户的 unionid 是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,unionid 是相同的。

要获取 unionid,需先注册微信开放平台并完成开发者认证,然后把应用绑定到微信开放平台。

二、UnionID 登录获取方式

小程序登录获取 unionid 主要有两种方式,有丰富公众号开发经验的童鞋,千万不要用公众号的思维直接带入,不然会给自己挖坑。下面详细说明:

1. wx.login + code2Session

小程序端调用 wx.login 获取 code 传给后端,后端通过 code2Session 接口获取 unionid,但要满足下面两种条件之一:

  • 开发者帐号下存在同主体的公众号,并且该用户已经关注了该公众号。
  • 开发者帐号下存在同主体的公众号或移动应用,并且该用户已经授权登录过该公众号或移动应用。

否则 code2Session 接口只会返回 openid 相关信息,不会返回 unionid,很明显单纯使用这种方式不能满足我们的需求。

code2Session 接口官方文档地址: https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html

2. wx.getUserInfo + 后端解密

小程序端调用 wx.getUserInfo 获取用户的加密数据 encrypted_data 和 iv 传给后端,后端通过解密获取 unionid。

效验及解密官方文档地址:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html

前后端可以通过两种方式配合实现最佳登录逻辑。


本文首发于马燕龙个人博客,欢迎分享,转载请标明出处。
马燕龙个人博客:https://www.mayanlong.com
马燕龙个人微博:http://weibo.com/imayanlong
马燕龙Github主页:https://github.com/yanlongma

自己撸一个 LaraDock(使用 Docker LNMP 部署 PHP 开发环境)

项目简介

Docker LNMP 是基于 docker-compose 开发的运行在 Docker 上的 LNMP 开发环境,包含 PHP、MySQL、Redis 等镜像并支持多版本切换,满足您的学习、开发和测试需求。

Github 地址:https://github.com/yanlongma/docker-lnmp

包含镜像

Docker LNMP 包含以下镜像,每种镜像支持多个版本:

  • nginx
  • php-fpm (7.3 - 7.2 - 7.1 - 5.6)
  • mysql (8.0 - 5.7 - 5.6)
  • mongo
  • redis (5.0 - 4.0)
  • memcached (1.5.16 - 1.5 - 1)

其中:

php-fpm 默认是 7.1 版本,如需使用其它版本,配置 .env 文件中 PHP_VERSION 即可;

mysql 默认是 5.7 版本,如需使用其它版本,配置 .env 文件中 MYSQL_VERSION 即可;

下载使用

Docker LNMP 默认将同级目录映射到 php-fpm 容器的工作目录,在项目的同级目录下载 Docker LNMP:

$ git clone https://github.com/yanlongma/docker-lnmp.git

进入 docker-lnmp 目录,生成配置文件 .env

$ cd docker-lnmp
$ cp env-template .env

如需映射到其它目录,配置 .env 文件中 WEB_ROOT_PATH 即可。

启动服务

在 docker-lnmp 目录,启动服务,命令如下:

$ docker-compose up -d nginx
Creating network "docker-lnmp_default" with the default driver
Creating docker-lnmp_mysql_1   ... done
Creating docker-lnmp_php-fpm_1 ... done
Creating docker-lnmp_nginx_1   ... done

nginx 默认会启动 php-fpm 和 mysql 服务,如需启动其它服务请手动添加,可选服务有 mongo、redis、memcached。

启动成功后,在 docker-lnmp 同级目录新建 phpinfo.php 文件,浏览器访问 http://localhost/phpinfo.php,则可看到 phpinfo() 相关信息。

关闭服务

在 docker-lnmp 目录,关闭服务,命令如下:

$ docker-compose down

构建服务

如修改 dockerfile 文件,需重新构建服务,如重新构建 php-fpm 命令如下:

$ docker-compose build php-fpm

建议先关闭服务,构建完成再重启服务。

虚拟主机

配置虚拟主机请参考 nignx/sites/yii.conf 文件,配置完需构建并重启服务。

License

MIT license

Chart.js 动态图表的使用

一、相关资料

1. 简介

Chart.js 是一个基于 HTML5 的简单的面向对象的图表库,支持包括 IE7/8 和所有现代浏览器。支持六种图标:曲线图(Linecharts)、柱状图(Barcharts)、雷达图(Radarcharts)、饼状图(Piecharts)、极坐标区域图(Polararea charts)以及圆环图(Doughnutcharts)。并且带有动画效果(animated),支持 retina 屏。

2. 官网

官网:https://www.chartjs.org/

二、示例代码

本案例演示了最近 24 小时的 PV/UV 实时数据,在线 DEMO

<!DOCTYPE html>
<html>
<head>
    <title>Chart.js 动态图表的使用</title>
    <script src="https://cdn.jsdelivr.net/npm/chart.js@2.8.0"></script>
</head>
<body>
    <div>
        <canvas id="chart"></canvas>
    </div>

    <script type="text/javascript">
    
        var dataLabels = ['1h', '2h', '3h', '4h', '5h', '6h', '7h', '8h', '9h', '10h', '11h', '12h', '13h', '14h', '15h', '16h', '17h', '18h', '19h', '20h', '21h', '22h', '23h', '0h'];
        var dataPV = [133058,253219,255194,233058,253219,277318,277714,273337,255194,277318,277714,273337,233058,253219,277318,253219,277318,277714,273337,255194,277714,273337,255194,293058];
        var dataUV = [10651,22039,23955,23754,22664,10651,22039,23765,23955,23754,22664,23765,23955,23754,22664,10651,22039,23765,10651,22039,23765,23955,23754,22664];

        var config = {
            type: 'line',
            data: {
                labels: dataLabels,
                datasets: [
                    {
                        label: 'PV',
                        data: dataPV,
                        backgroundColor: 'rgb(255, 99, 132)',
                        borderColor: 'rgb(255, 99, 132)',
                        fill: false,
                    },
                    {
                        label: 'UV',
                        data: dataUV, 
                        backgroundColor: 'rgb(75, 192, 192)',
                        borderColor: 'rgb(75, 192, 192)',
                        fill: false, 
                    }
                ]
            },
            options: {
                responsive: true,
                title: {
                    display: true,
                    text: 'PV/UV 实时统计'
                },
            }
        };

        var ctx = document.getElementById('chart').getContext('2d');
        var chart = new Chart(ctx, config);

        setInterval(function() {
            if (config.data.datasets.length > 0) {

                var last = parseInt(dataLabels[dataLabels.length - 1]);
                var label = last + 1;
                if (last >= 23) {
                    label = 0;
                }
                label = label + 'h';

                dataLabels.push(label);
                dataPV.push(getRandomNum(200000, 300000));
                dataUV.push(getRandomNum(10000, 80000));

                dataLabels.shift();
                dataPV.shift();
                dataUV.shift();

                chart.update();
            }
        }, 1000);

        function getRandomNum(min, max) {
            var range = max - min;
            var rand = Math.random();
            return(min + Math.round(rand * range));
        }

    </script>
</body>
</html>

本文首发于马燕龙个人博客,欢迎分享,转载请标明出处。
马燕龙个人博客:https://www.mayanlong.com
马燕龙个人微博:http://weibo.com/imayanlong
马燕龙Github主页:https://github.com/yanlongma

Nginx 系列 - 用 GoAccess 实现可视化并实时监控 access 日志

一、安装使用

1. 安装 GoAccess

本例使用源码安装,./configure 如果出错请看问题说明部分:

$ wget https://tar.goaccess.io/goaccess-1.3.tar.gz
$ tar -xzf goaccess-1.3.tar.gz
$ cd goaccess-1.3
$ ./configure --enable-utf8 --enable-geoip=legacy
$ make
# make install

2. 修改 Nginx 配置

nginx.conf 文件中新增以下配置:

location /report.html {
        alias /usr/local/nginx/html/report.html;
}

并重新加载配置文件:

$ /usr/local/nginx/sbin/nginx -s reload

3. 启动 GoAccess

在 nginx 安装目录下,执行 goaccess 命令:

$ cd /usr/local/nginx
$ goaccess ./logs/access.log -o ./html/report.html --real-time-html --time-format='%H:%M:%S' --date-format='%d/%b/%Y' --log-format=COMBINED
WebSocket server ready to accept new client connections

4. 访问服务

在浏览器中打开http://127.0.0.1/report.html,如出现以下页面则安装配置成功:

goaccess-report

二、问题说明

本处整理了安装 GoAccess 执行 configure 命令报错和解决办法。

1. 如出现以下报错则需安装 GeoIP:

configure: error: 
    *** Missing development files for the GeoIP library

2. 如出现以下报错则需安装 NCurses:

checking for mvaddwstr in -lncursesw... no
configure: error: *** Missing development libraries for ncursesw

3. 解决办法

官方已经给出了各个平台对应依赖包的名字(传送至官网),只需使用对应平台的包管理工具安装即可,如下图:

goaccess-distribution-package.png

如以上两个报错在 centos 下只需安装 geoip-devel 和 ncurses-devel 即可:

$ yum install geoip-devel ncurses-devel

4. 相关资料

GoAccess 官网:https://goaccess.io


本文首发于马燕龙个人博客,欢迎分享,转载请标明出处。
马燕龙个人博客:https://www.mayanlong.com
马燕龙个人微博:http://weibo.com/imayanlong
马燕龙Github主页:https://github.com/yanlongma

手把手教你使用 GitBook

一、简介

GitBook 是一个支持用 MarkDown 编写文档的软件,支持输出 HTML、PDF、eBook 格式文档。作为开发者我们一般会用它来写一些技术和接口文档。

GitBook 官网:https://www.gitbook.com

二、安装

1. 安装 Node.js

GitBook 是基于 Node.js 的命令行工具,需先下载安装 Node.js,检测是否安装成功:

$ node -v
v6.9.1

2. 安装 GitBook

使用 npm 安装 GitBook,命令如下:

$ npm install -g gitbook-cli

检测是否安装成功:

$ gitbook -V
CLI version: 2.3.2
GitBook version: 3.2.3

三、使用

1. 初始化

新建目录 book,在该目录下执行如下命令进行初始化:

$ gitbook init
warn: no summary file in this book 
info: create README.md 
info: create SUMMARY.md 
info: initialization is finished

该命令将会生成 README.mdSUMMARY.md 两个文件。其中 README.md 是对文档的简单介绍,可以用来作为封面,SUMMARY.md 是文档的目录结构。

也可以自己预先定义好 SUMMARY.md 中的目录结构,然后再用 init 命令初始化,程序将会根据我们的目录结构生成目录和文件,目录结构示例:

# Summary

* [Introduction](README.md)
* [Chapter1](chapter1/README.md)
    * [Section1.1](chapter1/section1.1.md)
    * [Section1.2](chapter1/section1.2.md)
* [Chapter2](chapter2/README.md)

2. 生成并预览

book 目录下执行如下命令,将会生成 HTML 文件并提供预览地址:

$ gitbook serve
Live reload server started on port: 35729
Press CTRL+C to quit ...

info: 7 plugins are installed 
info: loading plugin "livereload"... OK 
info: loading plugin "highlight"... OK 
info: loading plugin "search"... OK 
info: loading plugin "lunr"... OK 
info: loading plugin "sharing"... OK 
info: loading plugin "fontsettings"... OK 
info: loading plugin "theme-default"... OK 
info: found 1 pages 
info: found 0 asset files 
info: >> generation finished with success in 1.0s ! 

Starting server ...
Serving book on http://localhost:4000

在浏览器中打开 http://localhost:4000 即可看到初始化的文档。

如果只需生成 HTML 文件执行 build 命令即可,命令如下:

$ gitbook build

如果需要生成 PDF 文件,由于依赖 ebook-convert,需要安装 Calibre,安装配置完成后,执行如下命令即可:

$ gitbook pdf

3. 编辑器

现在支持实时预览的 markdown 编辑器有很多,可以根据自己的喜好选择,此处仅做推荐:

  • 官方编辑器 GitBook Editor
  • PhpStorm(不要问我为什么,我不会告诉你我是 PHP 程序猿)
  • Sublime Text(其实我写 MD 文档都不用预览,所以没给它装插件,龇牙)

四、配置

通过配置 book.json 中的参数,我们可以配置文档的很多信息,比如标题、侧边栏、插件等。
一个简单的 book.json 文件如下:

{
    "title": "GitBook 使用教程",
    "author": "Yanlong Ma <json_vip@163.com>",
    "description": "GitBook 使用教程 - 马燕龙个人博客",
    "generator": "site",
    "links": {
        "sidebar": {
            "马燕龙个人博客": "https://www.mayanlong.com"
        }
    }
}

下面介绍的插件也需要用到该配置。

五、插件

1. 自带插件

GitBook 默认带有五个插件:highlight、search、sharing、fontsettings、livereload。如果要去除自带的插件,可以在插件名称前面加”-“,如下:

"plugins": ["-search"]

2. 命名规范

GitBook 还支持许多插件,可以从 NPM 上搜索 GitBook 的插件,GitBook 推荐插件的命名方式如下,所以可以通过以下两种方式来搜索 GitBook 的插件或者主题:

gitbook-plugin-X: 插件
gitbook-theme-X: 主题

3. 安装插件

下面介绍如何安装 gitbook-plugin-prism 插件,在 NPM 搜索该插件,插件主页链接为 https://www.npmjs.com/package/gitbook-plugin-prism

首先安装该插件,命令如下:

$ npm i gitbook-plugin-prism

然后在 book.json 中添加该插件,同时禁用默认的 highlight,配置如下:

{
  "plugins": ["prism", "-highlight"]
}

可以根据自己的喜好,配置不同的主题:

"pluginsConfig": {
    "prism": {
        "css": [
            "prismjs/themes/prism-solarizedlight.css"
        ]
    }
}

本文首发于马燕龙个人博客,欢迎分享,转载请标明出处。
马燕龙个人博客:https://www.mayanlong.com
马燕龙个人微博:http://weibo.com/imayanlong
马燕龙Github主页:https://github.com/yanlongma

手把手教你发布自己的 Composer 包

一、前言

Composer 是 PHP 用来管理依赖(dependency)关系的工具。我们不仅要学会使用别人提供的包,更要学会制作和分享自己的软件包,下面演示如何创建一个自己的 Composer 包。

准备工作:

  1. 注册 Github 账号
  2. 注册 Packagist 账号

二、实践

本案例演示如何创建一个第三方消息推送(极光推送)的包。

1. 创建 Github 仓库

登录 Github,创建仓库 yanlongma/push,并将代码克隆到本地:

$ git clone https://github.com/yanlongma/push.git

2. 创建 Composer 配置文件

进入项目根目录,创建 Composer 配置文件 composer.json,可以使用命令 compser init 创建也可以手动创建,最终文件内容大体如下:

{
    "name": "yanlongma/push",
    "description": "Third party message push",
    "authors": [
        {
            "name": "Yanlong Ma"
        }
    ],
    "license": "MIT",
    "require": {
        "php": ">=5.4"
    },
    "autoload": {
        "psr-4": {
            "YanlongMa\\Push\\": "src/"
        }
    }
}

3. 提交代码到 Github

根据自己需要实现的功能编写代码,本项目最终项目结构如下:

.git/  
.gitignore  
composer.json  
README.md  
src/
    Client.php    
    JPush.php

代码编写完成且测试没问题后提交代码到 Github。

4. 发布包到 Packagist

登录 Packagist,检出 https://github.com/YanlongMa/push.git 仓库的代码,系统会根据仓库中 composer.json 文件自动设置包的相关信息。

5. 设置 Packagist 中的包自动更新

如果不设置自动同步,每次 Github 中的代码更新,需要在对应包中手动更新,所以建议设置自动更新。步骤如下:

  1. 进入 yanlongma/push 仓库,选择 "Settings -> Integrations & services";
  2. 点击 "Add service",选择 “Packagist”;
  3. 填写你的 Packagist 账号对应的信息(登录后点击查看https://packagist.org/profile/
  4. 配置完成后,点击右上角的“Test service”,如果出现 “Okay, the test payload is on its way.”,则说明配置成功。

6. 使用共享包

发布包到 Packagist 后,根据包名就可以搜索和使用该包了,在自己的项目中申明该包依赖:

$ composer require yanlongma/push

该包的具体使用可以查看 https://github.com/yanlongma/push

  • 发布包到 Packagist 后,可能过几分钟才能在客户端 search 到;
  • 没有打 tag 的要指定 dev,完整命令 composer require "yanlongma/push @dev"

本文首发于马燕龙个人博客,欢迎分享,转载请标明出处。
马燕龙个人博客:https://www.mayanlong.com
马燕龙个人微博:http://weibo.com/imayanlong
马燕龙Github主页:https://github.com/yanlongma

PHP 基础篇 - PHP 中 DES 加解密详解

一、简介

DES 是对称性加密里面常见一种,全称为 Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法。密钥长度是64位(bit),超过位数密钥被忽略。所谓对称性加密即加密和解密密钥相同,对称性加密一般会按照固定长度,把待加密字符串分成块,不足一整块或者刚好最后有特殊填充字符。

跨语言做 DES 加密解密经常会出现问题,往往是填充方式不对、编码不一致或者加密解密模式没有对应上造成。常见的填充模式有: pkcs5、pkcs7、iso10126、ansix923、zero。加密模式有:DES-ECB、DES-CBC、DES-CTR、DES-OFB、DES-CFB。

作为一个软件开发者,可以通过工具测试 DES 加密解密,这里推荐一个在线工具:http://tool.chacuo.net/cryptdes

二、实现

PHP 提供了 Mcrypt 系列函数来实现 DES 的加解密,但该扩展中的函数陆续被废弃,自 PHP 7.2.0 起,会移到 PECL。

所以本代码用了更通用的 OPENSSL 方式实现 DES 的加解密,具体的实现和使用代码如下:

<?php

/**
 * openssl 实现的 DES 加密类,支持各种 PHP 版本
 */
class DES
{
    /**
     * @var string $method 加解密方法,可通过 openssl_get_cipher_methods() 获得
     */
    protected $method;

    /**
     * @var string $key 加解密的密钥
     */
    protected $key;

    /**
     * @var string $output 输出格式 无、base64、hex
     */
    protected $output;

    /**
     * @var string $iv 加解密的向量
     */
    protected $iv;

    /**
     * @var string $options
     */
    protected $options;

    // output 的类型
    const OUTPUT_NULL = '';
    const OUTPUT_BASE64 = 'base64';
    const OUTPUT_HEX = 'hex';


    /**
     * DES constructor.
     * @param string $key
     * @param string $method
     *      ECB DES-ECB、DES-EDE3 (为 ECB 模式时,$iv 为空即可)
     *      CBC DES-CBC、DES-EDE3-CBC、DESX-CBC
     *      CFB DES-CFB8、DES-EDE3-CFB8
     *      CTR
     *      OFB
     *
     * @param string $output
     *      base64、hex
     *
     * @param string $iv
     * @param int $options
     */
    public function __construct($key, $method = 'DES-ECB', $output = '', $iv = '', $options = OPENSSL_RAW_DATA | OPENSSL_NO_PADDING)
    {
        $this->key = $key;
        $this->method = $method;
        $this->output = $output;
        $this->iv = $iv;
        $this->options = $options;
    }

    /**
     * 加密
     *
     * @param $str
     * @return string
     */
    public function encrypt($str)
    {
        $str = $this->pkcsPadding($str, 8);
        $sign = openssl_encrypt($str, $this->method, $this->key, $this->options, $this->iv);

        if ($this->output == self::OUTPUT_BASE64) {
            $sign = base64_encode($sign);
        } else if ($this->output == self::OUTPUT_HEX) {
            $sign = bin2hex($sign);
        }

        return $sign;
    }

    /**
     * 解密
     *
     * @param $encrypted
     * @return string
     */
    public function decrypt($encrypted)
    {
        if ($this->output == self::OUTPUT_BASE64) {
            $encrypted = base64_decode($encrypted);
        } else if ($this->output == self::OUTPUT_HEX) {
            $encrypted = hex2bin($encrypted);
        }

        $sign = @openssl_decrypt($encrypted, $this->method, $this->key, $this->options, $this->iv);
        $sign = $this->unPkcsPadding($sign);
        $sign = rtrim($sign);
        return $sign;
    }

    /**
     * 填充
     *
     * @param $str
     * @param $blocksize
     * @return string
     */
    private function pkcsPadding($str, $blocksize)
    {
        $pad = $blocksize - (strlen($str) % $blocksize);
        return $str . str_repeat(chr($pad), $pad);
    }

    /**
     * 去填充
     * 
     * @param $str
     * @return string
     */
    private function unPkcsPadding($str)
    {
        $pad = ord($str{strlen($str) - 1});
        if ($pad > strlen($str)) {
            return false;
        }
        return substr($str, 0, -1 * $pad);
    }

}


$key = 'key123456';
$iv = 'iv123456';

// DES CBC 加解密
$des = new DES($key, 'DES-CBC', DES::OUTPUT_BASE64, $iv);
echo $base64Sign = $des->encrypt('Hello DES CBC');
echo "\n";
echo $des->decrypt($base64Sign);
echo "\n";

// DES ECB 加解密
$des = new DES($key, 'DES-ECB', DES::OUTPUT_HEX);
echo $base64Sign = $des->encrypt('Hello DES ECB');
echo "\n";
echo $des->decrypt($base64Sign);

三、相关链接


本文首发于马燕龙个人博客,欢迎分享,转载请标明出处。
马燕龙个人博客:https://www.mayanlong.com
马燕龙个人微博:http://weibo.com/imayanlong
马燕龙Github主页:https://github.com/yanlongma

macOS 上安装 PECL

一、简介

PECL(The PHP Extension Community Library)是 PHP 扩展的存储库,为 PHP 所有的扩展提供提供托管和下载服务。

通过 PEAR(PHP Extension and Application Repository)的 Package Manager 的安装管理方式,可以对 PECL 扩展进行下载和安装。

二、安装

官方提供了 PEAR 在各个平台的安装方式,直接看官方文档的请进【传送门】,macOS 平台官方安装翻译如下。

1. 下载 PEAR

使用 curl 命令下载即可:

$ curl -O https://pear.php.net/go-pear.phar

2. 安装 PEAR

使用 sudo 授权进行安装:

$ sudo php -d detect_unicode=0 go-pear.phar

安装过程需要进行简单的配置,如下:

Below is a suggested file layout for your new PEAR installation.  To
change individual locations, type the number in front of the
directory.  Type 'all' to change all of them or simply press Enter to
accept these locations.

 1. Installation base ($prefix)                   : /usr
 2. Temporary directory for processing            : /tmp/pear/install
 3. Temporary directory for downloads             : /tmp/pear/install
 4. Binaries directory                            : /usr/bin
 5. PHP code directory ($php_dir)                 : /usr/share/pear
 6. Documentation directory                       : /usr/docs
 7. Data directory                                : /usr/data
 8. User-modifiable configuration files directory : /usr/cfg
 9. Public Web Files directory                    : /usr/www
10. System manual pages directory                 : /usr/man
11. Tests directory                               : /usr/tests
12. Name of configuration file                    : /private/etc/pear.conf

1-12, 'all' or Enter to continue: 1

输入 1,将安装根目录修改为 /usr/local/pear;
输入 4,将命令安装到 /usr/local/bin 目录;
其它选项默认即可,一路回车。

3. 检测是否安装成功

出现如下结果,则安装成功:

$ pear version
PEAR Version: 1.10.5
PHP Version: 7.1.7
Zend Engine Version: 3.1.0

三、相关连接

PECL 官方地址:http://pecl.php.net/
PEAR 官方地址:http://pear.php.net/


本文首发于马燕龙个人博客,欢迎分享,转载请标明出处。
马燕龙个人博客:https://www.mayanlong.com
马燕龙个人微博:http://weibo.com/imayanlong
马燕龙Github主页:https://github.com/yanlongma