通通不可说

生命永远充满希望

0%

1. 什么是HTTPS?

1.1 HTTPS的解释

HTTPS(Hypertext Transfer Protocol Secure)是HTTP协议的安全版本。它通过在HTTP和TCP之间添加一个安全层(通常是SSL/TLS),来确保数据在传输过程中的机密性和完整性。HTTPS主要用于保护敏感信息,如用户登录凭证、信用卡信息等,防止这些数据在传输过程中被窃取或篡改。

1.2 HTTPS与HTTP的区别

HTTPS和HTTP的主要区别包括:

  1. 安全性:HTTPS通过加密传输的数据,提供了更高的安全性,而HTTP传输的数据是明文的。
  2. 端口:HTTPS默认使用443端口,HTTP默认使用80端口。
  3. URL前缀:HTTPS的URL以”https://“开头,HTTP的URL以”http://“开头。

2. 什么是PKI?

2.1 PKI的定义

PKI(Public Key Infrastructure,公钥基础设施)是一个用于创建、管理、分发、使用、存储和撤销数字证书的系统。它提供了一个框架,使得在不安全的网络中,如互联网,可以安全地进行电子交易。PKI的核心是基于非对称加密技术,它使用一对密钥:公钥和私钥。公钥可以自由分发,而私钥则由所有者秘密保管。PKI通过数字证书来建立身份的可信度,这些证书由被称为证书颁发机构(CA)的可信第三方签发。

2.2 PKI的组件

PKI的主要组件包括:

  • 证书颁发机构(CA):负责签发和管理数字证书
  • 注册机构(RA):验证证书申请者的身份
  • 证书存储库:存储和分发证书及证书撤销列表(CRL)
  • 密钥对:包括公钥和私钥
  • 终端实体:证书的使用者,如个人、组织或设备

2.3 每个组件在建立信任中的角色

在PKI中,每个组件在建立信任过程中扮演着不同但相互关联的角色:

  • 证书颁发机构(CA):作为信任的根源,CA负责验证申请者的身份,并签发数字证书,从而建立可信的身份认证体系。
  • 注册机构(RA):作为CA的代理,RA负责收集和验证证书申请者的信息,确保申请者的身份真实可靠,从而支持CA的信任决策。
  • 证书存储库:通过提供证书和证书撤销列表的公开访问,使得依赖方能够验证证书的有效性,从而维护整个PKI系统的信任状态。
  • 密钥对:公钥用于加密数据和验证数字签名,私钥用于解密数据和创建数字签名,共同构成了PKI中的加密和身份验证基础。
  • 终端实体:作为证书的实际使用者,终端实体通过使用其数字证书来证明自己的身份,从而在网络通信中建立可信的关系。

2.4 数字证书和证书链

在PKI系统中,数字证书是一种电子文档,用于证明公钥的所有权。它包含了公钥、所有者的身份信息以及颁发机构的数字签名。数字证书的主要作用是建立一个可信的身份验证机制,确保通信双方的身份真实可靠。证书通常遵循X.509标准,包含诸如版本号、序列号、签名算法、颁发者、有效期、主体、公钥信息等字段。

image.png

image.png

image.png

数字签名是为了确保数据发送者的合法身份,也可以确保数据内容未遭到篡改,保证数据完整性。与手写签名不同的是,数字签名会随着文本数据的变化而变化。具体到数字证书的应用场景,数字签名的生成和验证流程如下:

  1. 服务器对证书内容进行信息摘要计算(常用算法有 SHA-256 等),得到摘要信息,然后用私钥加密该摘要信息,生成数字签名。
  2. 服务器将数字证书和数字签名一起发送给客户端。
  3. 客户端使用公钥解密数字签名,获得摘要信息。
  4. 客户端使用相同的信息摘要算法重新计算证书的摘要信息,然后将两个摘要信息进行比对。如果相同,则证明证书未被篡改;否则,证书验证失败。

证书链是PKI系统中用于建立信任的重要机制。它是一系列数字证书的集合,从终端实体的证书开始,通过一个或多个中间证书颁发机构(CA)的证书,最终连接到根CA的证书。这种链式结构允许验证者通过追溯证书链来确认一个证书的有效性和可信度。在实际应用中,证书链的验证过程是自动进行的,通常由浏览器或操作系统完成,为用户提供了无缝的安全体验。

image.png

3. PKI如何与HTTPS配合工作?

3.1 SSL/TLS协议的解释

SSL/TLS(Secure Sockets Layer/Transport Layer Security)是一种加密协议,用于在互联网上安全地传输数据。它在应用层和传输层之间提供了一个安全层,确保了数据的机密性、完整性和认证。SSL是TLS的前身,虽然现在通常使用TLS,但人们仍习惯性地将其称为SSL。这个协议通过使用数字证书、公钥加密和对称加密的组合来实现安全通信。

3.2 SSL/TLS握手过程的概述

image.png

  1. “Client Hello” 消息:客户端通过发送”Client Hello”消息向服务器发起握手请求。该消息包含客户端支持的 TLS 版本、密码套件选项,以及一个”client random”随机字符串。

  2. “Server Hello” 消息:服务器回应”Server Hello”消息,包含数字证书、服务器选择的密码套件和”server random”随机字符串。

  3. 验证:客户端验证服务器的证书,确保其身份合法。验证过程包括:

    a) 检查数字签名

    b) 验证证书链

    c) 检查证书有效期

    d) 检查证书撤销状态(撤销意味着证书已失效)

  4. “Premaster Secret” 生成:客户端生成”premaster secret”(预主密钥)并用服务器的公钥加密,发送给服务器。只有拥有对应私钥的服务器才能解密。

  5. 私钥解密:服务器使用私钥解密”premaster secret”。

  6. 会话密钥生成:客户端和服务器使用client random、server random和premaster secret,通过相同算法生成共享的会话密钥KEY

  7. 客户端就绪:客户端发送经会话密钥KEY加密的”Finished”消息。

  8. 服务器就绪:服务器也发送经会话密钥KEY加密的”Finished”消息。

  9. 安全通信建立:握手完成,双方使用对称加密进行后续安全通信。

3.3** HTTPS **加密、解密、验证及数据传输过程

image.png

HTTPS 的整个通信过程可分为两大阶段:证书验证和数据传输。数据传输阶段又可细分为非对称加密和对称加密两个阶段。以下是具体流程的讲解:

  1. 客户端请求 HTTPS 网址,连接到服务器的 443 端口(HTTPS 默认端口,类似于 HTTP 的 80 端口)。

  2. 采用 HTTPS 协议的服务器必须拥有一套数字证书(CA 证书)。这些证书需要申请,由专门的数字证书认证机构(CA)经过严格审核后颁发。证书价格因安全级别而异,级别越高价格越贵。颁发证书时会生成一对密钥:私钥由服务端保密存储,公钥附在证书信息中可公开。证书还包含一个电子签名,用于验证证书的完整性和真实性,防止篡改。

  3. 服务器响应客户端请求,将证书传递给客户端。证书包含公钥和其他信息,如证书颁发机构信息、公司信息和有效期等。在 Chrome 浏览器中,点击地址栏的锁图标再点击”证书”可查看详细信息。

  4. 客户端解析并验证证书。如果证书不是由可信机构颁发、证书域名与实际域名不符,或证书已过期,客户端会向用户显示警告,由用户决定是否继续通信。这就是”您的连接不是私密连接”的提示原因。

    若证书无问题,客户端从中提取服务器的公钥 A,并生成一个随机码 KEY,用公钥 A 加密。

  5. 客户端将加密后的随机码 KEY 发送给服务器,作为后续对称加密的密钥。

  6. 服务器收到随机码 KEY 后,用私钥 B 解密。至此,客户端和服务器建立了安全连接,解决了对称加密的密钥泄露问题。

  7. 服务器使用密钥(随机码 KEY)对数据进行对称加密并发送给客户端,客户端用相同的密钥解密数据。

  8. 双方使用对称加密安全地传输所有数据。

4. 如何获取SSL证书?

4.1 SSL证书的类型(扩展验证、组织验证、域验证)

SSL证书主要分为三种类型:

  • 扩展验证(EV)证书:提供最高级别的安全性和信任,需要严格的身份验证过程。
  • 组织验证(OV)证书:验证申请者的组织和域名所有权,提供中等级别的信任。
  • 域名验证(DV)证书:仅验证域名所有权,是最基本的SSL证书类型,通常用于小型网站或个人博客。

这些证书类型在验证过程、颁发速度和价格上有所不同,用户可以根据自身需求选择合适的类型。

4.2 获取和安装SSL证书的步骤

获取和安装SSL证书通常包括以下步骤:

  1. 选择证书类型和证书颁发机构(CA)
  2. 生成证书签名请求(CSR)和私钥
  3. 提交CSR给CA并完成验证过程
  4. 接收和下载SSL证书
  5. 在服务器上安装SSL证书和私钥
  6. 配置服务器以使用SSL/TLS
  7. 测试SSL配置并确保正常工作

具体步骤可能因CA和服务器类型而略有不同,但总体流程大致相同。

5. 应用案例

5.1 Nginx如何配置SSL?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
server {
# 服务器端口使用443,开启ssl, 这里ssl就是上面安装的ssl模块
listen 443 ssl;
# 域名,多个以空格分开
server_name www.example.com;

# ssl证书地址
ssl_certificate /usr/local/nginx/cert/ssl.pem; # pem文件的路径
ssl_certificate_key /usr/local/nginx/cert/ssl.key; # key文件的路径

# ssl验证相关配置
ssl_session_timeout 5m; #缓存有效期
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; #加密算法
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #安全链接可选的加密协议
ssl_prefer_server_ciphers on; #使用服务器端的首选算法

location / {
root html;
index index.html index.htm;
}
}

5.2 RabbitMQ如何配置SSL?

  1. 修改配置文件

配置文件/data/rabbitmq/conf.d/10-defaults.conf增加如下示例内容。禁用明文认证方式,启动SSL证书认证。

服务端证书:
ca/cacert.pem #CA证书
server/rabbitmq-server.cert.pem #服务端公钥
server/rabbitmq-server.key.pem #服务端私钥
客户端证书:
client/rabbitmq-client.keycert.p12 #客户端PKCS12证书
client/rabbitmqTrustStore #服务端JKS格式公钥
记录生成证书步骤中生成客户端证书时的密码,客户端应用程序需要。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
listeners.ssl.default=5671
ssl_options.cacertfile=/etc/rabbitmq/conf.d/ssl/cacert.pem
ssl_options.certfile=/etc/rabbitmq/conf.d/ssl/rabbitmq-server.cert.pem
ssl_options.keyfile=/etc/rabbitmq/conf.d/ssl/rabbitmq-server.key.pem

ssl_options.verify=verify_peer
ssl_options.fail_if_no_peer_cert=true
ssl_options.versions.1=tlsv1.2
ssl_options.versions.2=tlsv1.1

ssl_options.ciphers.1 = ECDHE-ECDSA-AES256-GCM-SHA384
ssl_options.ciphers.2 = ECDHE-RSA-AES256-GCM-SHA384
ssl_options.ciphers.3 = ECDHE-ECDSA-AES256-SHA384
ssl_options.ciphers.4 = ECDHE-RSA-AES256-SHA384
ssl_options.ciphers.5 = ECDHE-ECDSA-DES-CBC3-SHA
ssl_options.ciphers.6 = ECDH-ECDSA-AES256-GCM-SHA384
ssl_options.ciphers.7 = ECDH-RSA-AES256-GCM-SHA384
ssl_options.ciphers.8 = ECDH-ECDSA-AES256-SHA384
ssl_options.ciphers.9 = ECDH-RSA-AES256-SHA384
ssl_options.ciphers.10 = DHE-DSS-AES256-GCM-SHA384
ssl_options.ciphers.11= DHE-DSS-AES256-SHA256
ssl_options.ciphers.12 = AES256-GCM-SHA384
ssl_options.ciphers.13 = AES256-SHA256
ssl_options.ciphers.14 = ECDHE-ECDSA-AES128-GCM-SHA256
ssl_options.ciphers.15 = ECDHE-RSA-AES128-GCM-SHA256
ssl_options.ciphers.16 = ECDHE-ECDSA-AES128-SHA256
ssl_options.ciphers.17 = ECDHE-RSA-AES128-SHA256
ssl_options.ciphers.18 = ECDH-ECDSA-AES128-GCM-SHA256
ssl_options.ciphers.19= ECDH-RSA-AES128-GCM-SHA256
ssl_options.ciphers.20 = ECDH-ECDSA-AES128-SHA256
ssl_options.ciphers.21 = ECDH-RSA-AES128-SHA256
ssl_options.ciphers.22 = DHE-DSS-AES128-GCM-SHA256
ssl_options.ciphers.23 = DHE-DSS-AES128-SHA256
ssl_options.ciphers.24 = AES128-GCM-SHA256
ssl_options.ciphers.25 = AES128-SHA256
ssl_options.ciphers.26 = ECDHE-ECDSA-AES256-SHA
ssl_options.ciphers.27 = ECDHE-RSA-AES256-SHA
ssl_options.ciphers.28 = DHE-DSS-AES256-SHA
ssl_options.ciphers.29 = ECDH-ECDSA-AES256-SHA
ssl_options.ciphers.30 = ECDH-RSA-AES256-SHA
ssl_options.ciphers.31= AES256-SHA
ssl_options.ciphers.32 = ECDHE-ECDSA-AES128-SHA
ssl_options.ciphers.33 = ECDHE-RSA-AES128-SHA
ssl_options.ciphers.34 = DHE-DSS-AES128-SHA
ssl_options.ciphers.35 = DHE-DSS-AES128-SHA256
ssl_options.ciphers.36 = ECDH-ECDSA-AES128-SHA
ssl_options.ciphers.37 = ECDH-RSA-AES128-SHA
ssl_options.ciphers.38 = AES128-SHA
ssl_cert_login_from = common_name

auth_mechanisms.1 = EXTERNAL
  1. 启用SSL插件

进入RabbitMQ容器或服务器实际安装路径,执行以下命令

#启用rabbitmq_auth_mechanism_ssl作为EXTERNAL认证机制的实现

rabbitmq-plugins enable rabbitmq_auth_mechanism_ssl

#查看启动结果

rabbitmq-plugins list

image.png

  1. 添加证书登录用户与授权(重要)

#添加证书登录用户(用户名要与客户端证书名称前缀一致),密码任意

rabbitmqctl add_user 'rabbitmq-client' '1234567'

#给rabbitmq-client用户虚拟主机/的所有权限,如需其他虚拟主机替换/

rabbitmqctl set_permissions -p "/" "rabbitmq-client" ".*" ".*" ".*"

  1. SpringBoot集成

增加证书后对jdk版本有要求,必须jdk8u330以上

增加RabbitFanoutExchangeConfig.java和配置内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Configuration
public class RabbitFanoutExchangeConfig {
@Autowired
RabbitProperties rabbitProperties;
@Autowired
CachingConnectionFactory cachingConnectionFactory;

/
* 解决安全扫描 AMQP明文登录漏洞 仅当rabbitmq启用ssl时并且配置证书时,显式设置EXTERNAL认证机制<br/>
* EXTERNAL认证机制使用X509认证方式,服务端读取客户端证书中的CN作为登录名称,同时忽略密码
*/
@PostConstruct
public void rabbitmqSslExternalPostConstruct() {
RabbitProperties.Ssl ssl = rabbitProperties.getSsl();
if (ssl == null || ssl.getEnabled() == null) {
return;
}
boolean rabbitSslEnabled = TypeUtils.castToBoolean(ssl.getEnabled());
boolean rabbitSslKeyStoreExists = ssl.getKeyStore() != null;
if (rabbitSslEnabled && rabbitSslKeyStoreExists) {
cachingConnectionFactory.getRabbitConnectionFactory().setSaslConfig(DefaultSaslConfig.EXTERNAL);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
spring:
rabbitmq:
host: 192.168.1.x #改为实际ip
port: 5601 # 改为实际端口
virtual-host: host_test
ssl:
#启用rabbitmq客户端SSL连接
enabled: true
#客户端PKCS12证书,外部证书使用file替换classpath字样并调整为绝对路径
key-store: classpath:ssl/rabbitmq-client.keycert.p12
#客户端证书密码,如果生成证书时密码有变化则要替换
key-store-password: 123456
#公钥证书及类型,外部证书使用file替换classpath字样并调整为绝对路径
trust-store: classpath:ssl/rabbitmqTrustStore
trust-store-type: JKS
trust-store-password: 123456
#不校验主机名,默认开启会导致连接失败
verify-hostname: false
  1. 证书生成方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#克隆生成证书的仓库到当前目录
git clone --depth 1 https://github.com/hellxz/CMF-AMQP-Configuration.git
cd CMF-AMQP-Configuration/ssl
#生成ca证书,“MyRabbitMQCA”为自定义名称,名称任意。在当前目录下生成ca目录
sh setup_ca.sh MyRabbitMQCA
#生成服务端证书,第一个参数是服务端证书前缀,第二个参数是密码。密码任意,在当前目录下生成server目录
sh make_server_cert.sh rabbitmq-server 123456
#生成客户端证书,第一个参数是客户端证书前缀(同时也是rabbitmq用户名),第二个参数是密码。**密码任意需要记录**,在当前目录下生成client目录
sh create_client_cert.sh rabbitmq-client 654321
#生成JKS格式服务端公钥证书到client目录。其中-alias后为别称,-file后是服务端公钥位置,-keystore后是输出JSK证书位置,此处相对路径 123456为密码
keytool -import -alias rabbitmq-server \
-file server/rabbitmq-server.cert.pem \
-keystore client/rabbitmqTrustStore -storepass 123456
#输入y回车

5.3 MySQL如何配置SSL?

从MySql服务器的安装目录中取出ca.pem、client-cert.pem、client-key.pem,执行下面三个命令生成SpringBoot客户端所需要的两个证书文件mysqlTrustStore.jks、mysql-client.jks,注意第二个命令和第三个命令用到的密码(标红部分)需要相同。若使用其他客户端可直接用pem文件。

1
2
3
keytool -importcert -alias Cacert -file ca.pem  -keystore mysqlTrustStore.jks -storepass 123456
openssl pkcs12 -export -in client-cert.pem -inkey client-key.pem -name "mysqlclient" -passout pass:123456 -out client-keystore.p12
keytool -importkeystore -srckeystore client-keystore.p12 -srcstoretype pkcs12 -srcstorepass 123456 -destkeystore mysql-client.jks -deststoretype JKS -deststorepass 123456

SpringBoot配置文件示例,如果是磁盘路径,将classpath:改为file:

1
2
3
4
5
6
7
8
9
ssl:
cert:
path: ssl/mysql/prod/mysql-client.jks
ca:
path: ssl/mysql/prod/mysqlTrustStore.jks
config: true&verifyServerCertificate=true&requireSSL=true&clientCertificateKeyStoreUrl=classpath:${ssl.cert.path}&clientCertificateKeyStorePassword=123456&trustCertificateKeyStoreUrl=classpath:${ssl.ca.path}&trustCertificateKeyStorePassword=123456
spring:
datasource:
url: jdbc:mysql://xxxx:xxx/mtg_portal?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&tinyInt1isBit=false&useSSL=${ssl.config}1

6. 国密算法与HTTPS

6.1 国密算法的介绍

image.png

6.2 国密算法与HTTPS的结合

国密算法与HTTPS的结合主要体现在以下几个方面:首先,国密算法可以替代传统的RSA和ECC算法,用于HTTPS中的密钥交换和数字签名。其次,国密算法可以作为HTTPS中的对称加密算法,用于加密传输的数据。最后,国密算法还可以用于HTTPS证书的生成和验证,以提高安全性和合规性。

image.png

如何用Nginx反向代理openAI接口

最近国内很多地方都无法直接访问openAI的接口,从而无法接入ChatGPT,反向代理是一种解决方案。

反向代理是一种常见的服务器配置,通过它可以将客户端的请求转发给后端的服务。在这个教程中,我们将学习如何使用Nginx反向代理来访问OpenAI API。

步骤1:安装Nginx

首先,我们需要安装Nginx。在Ubuntu上,可以使用以下命令安装:

1
2
sudo apt-get update
sudo apt-get install nginx

如果有特殊的module需要添加(例如stream),可以自行编译安装,这里不做介绍。

在安装完成后,可以使用以下命令启动Nginx服务:

1
sudo systemctl start nginx

步骤2:配置Nginx

接下来,我们需要配置Nginx来反向代理OpenAI API。在Nginx的配置文件中添加以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
server {
listen 80;
server_name {your_domain_name};
location / {
proxy_pass https://api.openai.com/;
proxy_set_header Host api.openai.com;
proxy_set_header Connection '';
proxy_http_version 1.1;
chunked_transfer_encoding off;
proxy_buffering off;
proxy_cache off;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

{your_domain_name}替换为您自己的域名。其中以下几项是为了保证使用stream参数请求时,EventSource类型响应的流畅输出。

1
2
3
4
5
proxy_set_header Connection '';
proxy_http_version 1.1;
chunked_transfer_encoding off;
proxy_buffering off;
proxy_cache off;

步骤3:测试反向代理

现在,我们可以测试反向代理是否正常工作。使用以下命令重新加载Nginx配置:

1
sudo systemctl reload nginx

然后,可以使用以下命令测试反向代理:

1
curl http://{your_domain_name}/v1/api/completions?prompt=Hello%2C%20my%20name%20is%20John%20and%20I%20am

如果一切正常,您应该能够收到来自OpenAI的响应。

如果nginx出现形如[error] 27648#27648: *49512 SSL_do_handshake() failed (SSL: error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:SSL alert number 40) while SSL handshaking to upstream的错误日志,可以在代理配置添加如下配置:

1
proxy_ssl_server_name on;

完整的https配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
server {
listen 443 ssl;
server_name {your_domain_name};
ssl_certificate {your_cert_path};
ssl_certificate_key {your_cert_key_path};
ssl_session_cache shared:le_nginx_SSL:1m;
ssl_session_timeout 1440m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+ECDSA+AES128:EECDH+aRSA+AES128:RSA+AES128:EECDH+ECDSA+AES256:EECDH+aRSA+AES256:RSA+AES256:EECDH+ECDSA+3DES:EECDH+aRSA+3DES:RSA+3DES:!MD5;
location / {
proxy_pass https://api.openai.com/;
proxy_ssl_server_name on;
proxy_set_header Host api.openai.com;
proxy_set_header Connection '';
proxy_http_version 1.1;
chunked_transfer_encoding off;
proxy_buffering off;
proxy_cache off;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

结论

在本教程中,我们学习了如何使用Nginx反向代理来访问OpenAI API。主要是保留EventSource类型响应的流畅输出功能,以提供良好的用户使用体验。

对python3 requests包的网络请求抓包时,https请求会报错,错误如下:

1
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed

这时需要增加抓包工具用到的自签名证书,步骤如下:

  1. 终端执行命令python3 -m certifi,查看CA证书存放位置
  2. 将抓包工具生成的自签名证书内容(pem格式)复制到上面提示的文件中

Elasticsearch介绍

全文搜索属于最常见的需求,开源的 Elasticsearch 是目前全文搜索引擎的首选。它可以快速地储存、搜索和分析海量数据。维基百科、Stack Overflow、Github 都采用它。

Elasticsearch 的底层是开源库 Lucene。Elasticsearch 是 Lucene 的封装,提供了 REST API 的操作接口,开箱即用。

原理简介参考终于有人把Elasticsearch原理讲透了!

  1. 基础概念

    1. 节点 Node、集群 Cluster 和分片 Shards

      ElasticSearch 是分布式数据库,允许多台服务器协同工作,每台服务器可以运行多个实例。单个实例称为一个节点(node),一组节点构成一个集群(cluster)。

      分片是底层的工作单元,文档保存在分片内,分片又被分配到集群内的各个节点里,每个分片仅保存全部数据的一部分。

    2. 索引 Index、类型 Type 和文档 Document

      方便理解,对比我们比较熟悉的 MySQL 数据库:

      index → db

      type → table

      document → row

      这是一个不恰当的比喻。在数据库中,table之间是相互独立的,两个table中相同名字的column之间无关联。

      但在Elasticsearch中,Index下不同Type中Document的同名field是同一个,必须要有相同的映射定义。具体参考官方文档

  2. 使用 RESTful API 与 Elasticsearch 进行交互

    所有其他语言可以使用 RESTful API 通过默认端口 9200 和 Elasticsearch 进行通信。一个 Elasticsearch 请求和任何 HTTP 请求一样由若干相同的部件组成:

    curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'

    被 < > 标记的部件:

    部件名作用

    VERB:适当的 HTTP 方法 或 谓词 : GET、 POST、 PUT、 HEAD 或者 DELETE。

    PROTOCOL:http 或者 https

    HOST:Elasticsearch 集群中任意节点的主机名,或者用 localhost 代表本地机器上的节点。

    PORT:运行 Elasticsearch HTTP 服务的端口号,默认是 9200 。

    PATH:API 的终端路径(例如 _count 将返回集群中文档数量)。Path 可能包含多个组件,例如:_cluster/stats 和 _nodes/stats/jvm 。

    QUERY_STRING:任意可选的查询字符串参数 (例如 ?pretty 将格式化地输出 JSON 返回值,使其更容易阅读)

    BODY:一个 JSON 格式的请求体 (如果请求需要的话)

    示例

    1
    2
    3
    4
    5
    6
    curl -XGET 'http://localhost:9200/_count?pretty' -d '
    {
    "query": {
    "match_all": {}
    }
    }'
  3. 文档管理(CRUD)

    1. 增加:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      POST /db/user/1
      {
      "username": "wmyskxz1",
      "password": "123456",
      "age": "22"
      }

      POST /db/user/2
      {
      "username": "wmyskxz2",
      "password": "123456",
      "age": "22"
      }

      这一段代码稍微解释一下,这其实就往索引为 db 类型为 user 的数据库中插入一条 id 为 1 的一条数据,这条数据其实就相当于一个拥有 username/password/age 三个属性的一个实体,就是 JSON 数据

      执行命令后,Elasticsearch 返回如下数据:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      # POST /db/user/1
      {
      "_index": "db",
      "_type": "user",
      "_id": "1",
      "_version": 1,
      "result": "created",
      "_shards": {
      "total": 2,
      "successful": 1,
      "failed": 0
      },
      "_seq_no": 2,
      "_primary_term": 1
      }

      # POST /db/user/2
      {
      "_index": "db",
      "_type": "user",
      "_id": "2",
      "_version": 1,
      "result": "created",
      "_shards": {
      "total": 2,
      "successful": 1,
      "failed": 0
      },
      "_seq_no": 1,
      "_primary_term": 1
      }

      version 是版本号的意思,当我们执行操作会自动加 1

    2. 删除:

      1
      DELETE /db/user/1

      Elasticsearch 返回数据如下:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      {
      "_index": "db",
      "_type": "user",
      "_id": "1",
      "_version": 2,
      "result": "deleted",
      "_shards": {
      "total": 2,
      "successful": 1,
      "failed": 0
      },
      "_seq_no": 1,
      "_primary_term": 1
      }

      这里就可以看到 version 变成了 2

    3. 修改:

      1
      2
      3
      4
      5
      6
      PUT /db/user/2
      {
      "username": "wmyskxz3",
      "password": "123456",
      "age": "22"
      }

      Elasticsearch 返回数据如下:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      {
      "_index": "db",
      "_type": "user",
      "_id": "2",
      "_version": 2,
      "result": "updated",
      "_shards": {
      "total": 2,
      "successful": 1,
      "failed": 0
      },
      "_seq_no": 2,
      "_primary_term": 1
      }
    4. 查询:

      1
      GET /db/user/2

      返回数据如下:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      {
      "_index": "db",
      "_type": "user",
      "_id": "2",
      "_version": 2,
      "found": true,
      "_source": {
      "username": "wmyskxz3",
      "password": "123456",
      "age": "22"
      }
      }
  4. 搜索

    请求_search接口时,某些客户端不支持GET请求中有Body,需将GET改为POST。

    先往 Elasticsearch 中插入一些数据:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    PUT /movies/movie/1
    {
    "title": "The Godfather",
    "director": "Francis Ford Coppola",
    "year": 1972,
    "genres": [
    "Crime",
    "Drama"
    ]
    }

    PUT /movies/movie/2
    {
    "title": "Lawrence of Arabia",
    "director": "David Lean",
    "year": 1962,
    "genres": [
    "Adventure",
    "Biography",
    "Drama"
    ]
    }

    PUT /movies/movie/3
    {
    "title": "To Kill a Mockingbird",
    "director": "Robert Mulligan",
    "year": 1962,
    "genres": [
    "Crime",
    "Drama",
    "Mystery"
    ]
    }

    PUT /movies/movie/4
    {
    "title": "Apocalypse Now",
    "director": "Francis Ford Coppola",
    "year": 1979,
    "genres": [
    "Drama",
    "War"
    ]
    }

    PUT /movies/movie/5
    {
    "title": "Kill Bill: Vol. 1",
    "director": "Quentin Tarantino",
    "year": 2003,
    "genres": [
    "Action",
    "Crime",
    "Thriller"
    ]
    }

    PUT /movies/movie/6
    {
    "title": "The Assassination of Jesse James by the Coward Robert Ford",
    "director": "Andrew Dominik",
    "year": 2007,
    "genres": [
    "Biography",
    "Crime",
    "Drama"
    ]
    }

    现在已经把一些电影信息放入了索引,可以通过搜索看看是否可找到它们。 为了使用 ElasticSearch 进行搜索,我们使用 _search 端点,可选择使用索引和类型。

    也就是说,按照以下模式向URL发出请求://_search。其中,index 和 type 都是可选的。

    换句话说,为了搜索电影,可以对以下任一URL进行POST请求:

    http://localhost:9200/_search - 搜索所有索引和所有类型。

    http://localhost:9200/movies/_search - 在电影索引中搜索所有类型

    http://localhost:9200/movies/movie/_search - 在电影索引中显式搜索电影类型的文档。

    搜索请求正文和ElasticSearch查询DSL

    如果只是发送一个请求到上面的URL,我们会得到所有的电影信息。为了创建更有用的搜索请求,还需要向请求正文中提供查询。

    请求正文是一个JSON对象,除了其它属性以外,它还要包含一个名称为 “query” 的属性,这就可使用ElasticSearch的查询DSL。

    1
    { "query": { //Query DSL here } }

    DSL是ElasticSearch自己基于JSON的域特定语言,可以在其中表达查询和过滤器。可以把它简单同SQL对应起来,相当于条件语句。

    基本自由文本搜索

    查询DSL具有一长列不同类型的查询可以使用。 对于“普通”自由文本搜索,最有可能想使用一个名称为“查询字符串查询”。

    查询字符串查询是一个高级查询,有很多不同的选项,ElasticSearch将解析和转换为更简单的查询树。如果忽略了所有的可选参数,并且只需要给它一个字符串用于搜索,它可以很容易使用。

    现在尝试在两部电影的标题中搜索有“kill”这个词的电影信息:

    1
    2
    GET /_search 
    { "query": { "query_string": { "query": "kill" } } }
    指定搜索的字段

    在前面的例子中,使用了一个非常简单的查询,一个只有一个属性 “query” 的查询字符串查询。 如前所述,查询字符串查询有一些可以指定设置,如果不使用,它将会使用默认的设置值。

    这样的设置称为“fields”,可用于指定要搜索的字段列表。如果不使用“fields”字段,ElasticSearch查询将默认自动生成的名为 “_all” 的特殊字段,来基于所有文档中的各个字段匹配搜索。

    为了做到这一点,修改以前的搜索请求正文,以便查询字符串查询有一个 fields 属性用来要搜索的字段数组:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    GET /_search
    {
    "query": {
    "query_string": {
    "query": "ford",
    "fields": [
    "title"
    ]
    }
    }
    }
    条件匹配
    1
    2
    3
    4
    5
    6
    7
    8
    GET /_search
    {
    "query": {
    "match": {
    "year": 2007
    }
    }
    }
    过滤返回结果字段
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    GET /_search
    {
    "query": {
    "query_string": {
    "query": "ford",
    "fields": [
    "title"
    ]
    }
    },
    "_source": ["title","director"]
    }
    返回结果包含指定字段
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    GET /_search
    {
    "query": {
    "query_string": {
    "query": "ford",
    "fields": [
    "title"
    ]
    }
    },
    "_source": {
    "includes": [
    "title",
    "director"
    ]
    }
    }
    返回结果排除指定字段
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    GET /_search
    {
    "query": {
    "query_string": {
    "query": "ford",
    "fields": [
    "title"
    ]
    }
    },
    "_source": {
    "excludes": [
    "title",
    "director"
    ]
    }
    }
    排序
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    GET /_search
    {
    "query": {
    "query_string": {
    "query": "ford"
    }
    },
    "sort": {
    "year": {
    "order": "desc"
    }
    }
    }
    分页
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    GET /_search
    {
    "query": {
    "query_string": {
    "query": "ford"
    }
    },
    "sort": {
    "year": {
    "order": "desc"
    }
    },
    "from": 0,
    "size": 1
    }
    多条件查询-布尔值查询
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    GET /_search
    {
    "query": {
    "bool": {
    "must": [
    {
    "match": {
    "year": 2007
    }
    }
    ]
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    GET /_search
    {
    "query": {
    "bool": {
    "should": [
    {
    "match": {
    "year": 2007
    }
    }
    ]
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    GET /_search
    {
    "query": {
    "bool": {
    "must_not": [
    {
    "match": {
    "year": 2007
    }
    }
    ]
    }
    }
    }
    多条件查询-条件区间
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    GET /_search
    {
    "query": {
    "bool": {
    "must": [
    {
    "match": {
    "genres": "Drama"
    }
    }
    ],
    "filter": [
    {
    "range": {
    "year": {
    "gte": 2000
    }
    }
    }
    ]
    }
    }
    }
    精确查找

    term是代表完全匹配,即不进行分词器分析,文档中必须包含整个搜索的词汇。

    match和term的区别是,match查询的时候,elasticsearch会根据给定的字段提供合适的分析器,而term查询不会有分析器分析的过程,match查询相当于模糊匹配,只包含其中一部分关键词就行。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    GET /_search
    {
    "query": {
    "bool": {
    "must": [
    {
    "term": {
    "year": 2007
    }
    }
    ]
    }
    }
    }
    短语搜索

    match_phrase 称为短语搜索,要求所有的分词必须同时出现在文档中,同时位置必须紧邻一致。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    GET /_search
    {
    "query": {
    "bool": {
    "must": [
    {
    "match_phrase": {
    "director": "Andrew Dominik"
    }
    }
    ]
    }
    }
    }
  5. 聚合

    聚合分析简介

    1. ES聚合分析是什么?

    聚合分析是数据库中重要的功能特性,完成对一个查询的数据集中数据的聚合计算,如:找出某字段(或计算表达式的结果)的最大值、最小值,计算和、平均值等。ES作为搜索引擎兼数据库,同样提供了强大的聚合分析能力。

    对一个数据集求最大、最小、和、平均值等指标的聚合,在ES中称为指标聚合(metric)

    而关系型数据库中除了有聚合函数外,还可以对查询出的数据进行分组group by,再在组上进行指标聚合。在 ES 中group by 称为分桶桶聚合(bucketing)

    ES中还提供了矩阵聚合(matrix)、管道聚合(pipleline),但还在完善中。

    2. ES聚合分析查询的写法

    在查询请求体中以aggregations节点按如下语法定义聚合分析:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    {
    "aggregations" : {
    "<aggregation_name>" : { <!--聚合的名字 -->
    "<aggregation_type>" : { <!--聚合的类型 -->
    <aggregation_body> <!--聚合体:对哪些字段进行聚合 -->
    }
    [,"meta" : { [<meta_data_body>] } ]? <!--元 -->
    [,"aggregations" : { [<sub_aggregation>]+ } ]? <!--在聚合里面在定义子聚合 -->
    }
    [,"<aggregation_name_2>" : { ... } ]*<!--聚合的名字 -->
    }
    }

    aggregations 也可简写为 aggs

    3. 聚合分析的值来源

    聚合计算的值可以取字段的值,也可是脚本计算的结果

    指标聚合

    1. max min sum avg

    获取指定字段最大值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    GET /_search
    {
    "size": 0,
    "aggs": {
    "max_year": {
    "max": {
    "field": "year"
    }
    }
    }
    }

    获取查询结果指定字段最小值,按指定字段排序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    GET /_search
    {
    "size": 1,
    "query": {
    "match": {
    "genres": "Drama"
    }
    },
    "sort": [
    {
    "year": {
    "order": "asc"
    }
    }
    ],
    "aggs": {
    "max_year": {
    "min": {
    "field": "year"
    }
    }
    }
    }

    指定field,在脚本中用_value 取字段的值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    {
    "size": 0,
    "aggs": {
    "sum_year": {
    "sum": {
    "field": "year",
    "script": {
    "source": "_value * 2"
    }
    }
    }
    }
    }
    2. 文档计数

    注意接口地址为_count

    1
    2
    3
    4
    5
    6
    GET /_count
    {
    "query": {
    "match_all": {}
    }
    }
    3. value_count 统计某字段有值的文档数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    GET /_search
    {
    "size": 0,
    "aggs": {
    "year_count": {
    "value_count": {
    "field": "year"
    }
    }
    }
    }
    4. cardinality 值去重计数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    GET /_search
    {
    "size": 0,
    "aggs": {
    "year_count": {
    "cardinality": {
    "field": "year"
    }
    }
    }
    }
    5. stats 统计 count max min avg sum 5个值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    GET /_search
    {
    "size": 0,
    "aggs": {
    "stats": {
    "stats": {
    "field": "year"
    }
    }
    }
    }
    6. extended_stats 高级统计,比stats多4个统计结果: 平方和、方差、标准差、平均值加/减两个标准差的区间
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    GET /_search
    {
    "size": 0,
    "aggs": {
    "stats": {
    "extended_stats": {
    "field": "year"
    }
    }
    }
    }
    7. percentiles 占比百分位对应的值统计
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    GET /_search
    {
    "size": 0,
    "aggs": {
    "percentiles": {
    "percentiles": {
    "field": "year"
    }
    }
    }
    }
    8. percentile_ranks 统计值小于等于指定值的文档占比
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    GET /_search
    {
    "size": 0,
    "aggs": {
    "percentiles": {
    "percentile_ranks": {
    "field": "year",
    "values": [
    2000,
    1990
    ]
    }
    }
    }
    }

    桶聚合

    1. Terms Aggregation 根据字段值项分组聚合
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    GET /_search
    {
    "size": 0,
    "aggs": {
    "year_terms": {
    "terms": {
    "field": "year"
    }
    }
    }
    }
    2. Filter Aggregation 对满足过滤查询的文档进行聚合计算
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    GET /_search
    {
    "size": 0,
    "aggs": {
    "filtered_year_terms": {
    "filter": {
    "match": {
    "genres": "Drama"
    }
    },
    "aggs": {
    "year_terms": {
    "terms": {
    "field": "year"
    }
    }
    }
    }
    }
    }

使用提示

  1. 新建文档索引若不指定字段类型映射,则默认用动态映射。string类型会映射成text。Elasticsearch中text与keyword的区别
  2. 新建文档索引时,如果有date字段,注意格式化结果和索引配置是否一致,若不一致新增索引文档会失败。
  3. es的服务端和客户端需要使用相同版本号,不通版本之间不兼容。

Elasticsearch Head插件

elasticsearch-head将是一款专门针对于elasticsearch的客户端工具,提供Chrome浏览器插件。仓库地址https://github.com/mobz/elasticsearch-head

Choose Runtime
CodeGlance
GitToolBox
Grep Console
Key Promoter X
Maven Helper
Rainbow Brackets
RestfulToolkit
String Manipulation

Trojan简介

trojan是近两年兴起的网络工具,项目官网https://github.com/trojan-gfw ,文档官网是 https://trojan-gfw.github.io/trojan 。与强调加密和混淆的SS/SSR等工具不同,trojan将通信流量伪装成互联网上最常见的https流量,从而有效防止流量被检测和干扰。

安装Trojan

终端输入如下命令:

1
sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/trojan-gfw/trojan-quickstart/master/trojan-quickstart.sh)"

该命令会下载最新版的trojan并安装。安装完毕后,trojan配置文件路径是 /usr/local/etc/trojan/config.json

重点关注以下属性:

  1. local_port:监听的端口,默认是443,下文中因为要将Trojan放在Nginx后,因此改为了别的端口;
  2. remote_addrremote_port:非trojan协议时,将请求转发处理的地址和端口。可以是任意有效的ip/域名和端口号,默认是本机和80端口。一般是伪装的web站点服务;
  3. password:密码。需要几个密码就填几行,最后一行结尾不能有逗号;
  4. certkey:域名的证书和密钥;
  5. key_password:默认没有密码(如果证书文件有密码就要填上)

根据自己的需求修改配置文件,保存,然后设置开机启动:systemctl enable trojan,并启动trojan: systemctl start trojan

配合nginx

Trojan 默认端口为443,接管了所有443端口的流量,其他请求则是通过Trojan转发到了其他服务,例如nginx。如果有需求是将Trojan放在nginx后面,由nginx统一接收流量再分发到Trojan和其他服务(web,v2ray),可以参考下面的配置。

要实现上面的需求,用到了nginx的stream_ssl_preread_module模块,具体资料可以参考 http://nginx.org/en/docs/stream/ngx_stream_ssl_preread_module.html
编译nginx时增加此模块。

1.将Trojan配置文件的local_port改为非443端口,例如9443。将nginx原有443端口的配置改为非443端口,例如8443
2.nginx.conf中添加如下配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
stream {
map $ssl_preread_server_name $name {
your.trojan.domain.address trojan;
default nginx;
}
upstream trojan {
server 127.0.0.1:9443;
}
upstream nginx {
server 127.0.0.1:8443;
}
server {
listen 443;
listen [::]:443;
proxy_pass $name;
ssl_preread on;
}
}

执行nginx -t测试配置文件,若提示nginx: [emerg] unknown directive "stream" in XXX,则在nginx.conf中的events区块前增加下面的语句

1
load_module /usr/lib/nginx/modules/ngx_stream_module.so;

具体原因参考 https://serverfault.com/questions/858067/unknown-directive-stream-in-etc-nginx-nginx-conf86

以上就是Trojan放在Nginx后面的配置流程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/env bash
ARGS=($*)
ADB="adb"
ANDROID_DEVICES=(`adb devices | sed '1d' | awk '{print $1}'`)
if [[ ${#ANDROID_DEVICES[@]} -gt 1 ]];then
for((j=0;j&lt;${#ANDROID_DEVICES[@]};j++))
do
echo $(expr ${j} + 1).${ANDROID_DEVICES[$j]}
done
while [[ $REPLY == "" || $REPLY == "0" ]];do
read -p "输入序号选择设备:"
done
ADB="adb -s "${ANDROID_DEVICES[$REPLY-1]}
fi
${ADB} ${ARGS[@]}

db.system.profile.find({"op" : "query","ns" : "poetries", millis : { $gt : 100 },ts : { $gt : new ISODate("2018-06-07T00:00:00.000Z"),$lt : new ISODate("2018-06-07T10:00:00.000Z")}}).pretty()


db.getProfilingLevel()   1为开启

为了方便填写git commit message,写了一个脚本用于读取jira上的issues,并列出选择作为commit message。 用到了git hook中的prepare-commit-msg 下面提供bash shell和python3两个版本 1.bash shell 此版本需要安装jq。

#!/bin/bash
USERNAME=`git config --get user.name`
if [ "$2" = "message" -o "$2" = "commit" ]
then
    exit 0
fi
exec 2> /dev/null
ISSUS=`curl -u jira:jira "http://localhost:8081/rest/api/2/search?jql=status+in+(Reopened,%20%22To%20Do%22,%20%22In%20Progress%22)%20AND%20assignee%20in%20($USERNAME)%20ORDER%20BY%20updated%20DESC" | jq '.issues[]|{summary: .fields.summary, key: .key}'`
KEYS=(`echo $ISSUS | jq '.key'`)
SUMMARYS_TMP=("`echo $ISSUS | jq '.summary'`")
IFS_old=$IFS
IFS=$'\n'
SUMMARYS=($SUMMARYS_TMP)
IFS='"'
number=${#KEYS[@]}
declare -a ISSUSES='()'
for((i=0;i<$number;i++))
do
    ISSUSES[$i]="${KEYS[$i]}${SUMMARYS[$i]}"
done
for((j=0;j<${#ISSUSES[@]};j++))
do
    let k=$j+1
    echo $k.${ISSUSES[$j]}
done
exec < /dev/tty
exec 2>&1
read -p "请输入编号:"
let answer=$REPLY-1
echo ${ISSUSES[$answer]} > $1
IFS=$IFS_old

2.python3

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys, os, re
from subprocess import check_output
import requests
import json

def query_report(user):
    s = requests.Session()
    s.post('http://localhost:8081/rest/auth/1/session', json={"username":"jira","password":"jira"})
    url = 'http://localhost:8081/rest/api/2/search?jql=status+in+(Reopened,%20%22To%20Do%22,%20%22In%20Progress%22)%20AND%20assignee%20in%20(' + user +')%20ORDER%20BY%20updated%20DESC'
    reports = s.get(url)
    return reports.json()

sys.stdin = open('/dev/tty')
# 收集参数
commit_msg_filepath = sys.argv[1]
if len(sys.argv) > 2:
    commit_type = sys.argv[2]
else:
    commit_type = ''
if len(sys.argv) > 3:
    commit_hash = sys.argv[3]
else:
    commit_hash = ''

if commit_type == "message":
    sys.exit(0)
# 检测我们所在的分支
branch = check_output(['git', 'symbolic-ref', '--short', 'HEAD']).strip()
branch = str(branch, 'utf-8')
print("On branch '%s'" % branch)
username = check_output(['git', 'config', '--get', 'user.name']).strip()
username = str(username, 'utf-8')
messages = []
if branch.startswith('master'):
    j = query_report(username)
    if j["total"] > 0:
        issues = j["issues"]
        for i in range(len(issues)):
            print(str(i+1) + ':' + issues[i]["key"] + " " + issues[i]["fields"]["summary"])
            messages.append(issues[i]["key"] + " " + issues[i]["fields"]["summary"]);
    number = input("请输入编号: ")
    message = messages[int(number) - 1]
    with open(commit_msg_filepath, 'w') as f:
        f.seek(0, 0)
        f.write(message)

1.先安装编译环境需要的软件

sudo apt-get install openjdk-7-jdk git-core gnupg flex bison gperf build-essential \

zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 \

lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z-dev ccache \

libgl1-mesa-dev libxml2-utils xsltproc unzip

2.同步代码

#新建目录
mkdir workspace
cd workspace
#链接为清华镜像,--repo-url是repo工具的地址,--no-repo-verify关闭repo工具验证
repo init -u https://aosp.tuna.tsinghua.edu.cn/platform/manifest -b android-6.0.1_r79 --repo-url=https://mirrors.tuna.tsinghua.edu.cn/git/git-repo --no-repo-verify
#-d用清单文件里的版本;-c当前分支;--no-tags不拉取tag
repo sync -d -c --no-tags

3.开始编译

#初始化环境
source build/envsetup.sh
#选择产品
lunch
#用16线程编译
make -j16