2021-08-12
-
nginx默认的配置文件名是nginx.conf,位置在/usr/local/nginx/conf
、/etc/niginx
、或/usr/local/etc/nginx
。通过nginx -c xxx.conf
来指定配置文件。
-
当nginx启动后,可以通过nginx -s signal
来控制nginx,signal可以是:
- stop:快速关闭;quit:正常关闭;reload:重新加载配置文件;reopen:重新打开配置文件。
-
配置文件可以包含多个server
块,他们之间通过监听的端口和服务名来区分。一旦nginx决定使用哪个服server来处理请求,他就会根据server块内location的指令来匹配请求头中的url,①精确匹配优先级最高,遇到就返回结果;②普通匹配会选择location中前缀最长的那个,和顺序无关;③当location中有正则表达式时,会优先匹配正则表达式(正则级别比普通匹配优先级高,但比精确匹配优先级低),正则表达式的匹配顺序按照文件中的物理顺序匹配,只要匹配到一条正则,就会返回结果;如果没有匹配,就会取普通匹配中最匹配的那个。
-
nginx的错误日志文件在usr/local/nginx/logs
、/var/log/nginx
常用配置
#
proxy_pass
#
- 使用
proxy_pass
来配置代理服务。代理服务的流程:server接收request->把request传给代理服务->获取代理服务的response->把response返回给客户端。
例:
#
server {
listen 80;
#不带 / 结尾:保留 location 匹配的路径,追加到代理地址后
location /api/ {
proxy_pass http://backend; # 请求 /api/test → http://backend/api/test
}
#带 / 结尾:proxy_pass 的 URL 若以 / 结尾,Nginx 会替换 location 匹配的部分。
location /api/ {
proxy_pass http://backend/; # 请求 /api/test → http://backend/test
}
#将会映射到/html/tool这个路径下
location /tool {
root html;
}
#正则表达式以~开始,这里是匹配图片
location ~ \.(gif|jpg|png)$ {
root /data/images;
}
}
参考链接:
Beginner’s Guide (nginx.org)
nginx配置代理yum源和apt源
#
- nginx配置文件中加入如下配置,启动nginx
server {
listen 8803;
#必须要有DNS解析的配置
resolver 114.114.114.114;
location / {
proxy_pass http://$http_host$request_uri;
}
}
- ubuntu中创建/etc/apt/apt.conf文件,填入如下内容
/etc/apt/apt.conf
随后执行apt update
即可完成代理。
Acquire::http::Proxy "http://131.110.99.99:8803/";
- centos或者rocky中,编辑文件
/etc/yum.conf
,加入proxy=http://131.110.99.99:8803
(完整文件如下所示),同时还需要将源配置文件中的https改成httpsed -i 's/https/http/g' /etc/yum.repos.d/*.repo
,随后执行yum makecache
即可。
[main]
proxy=http://131.110.99.99:8803
gpgcheck=1
installonly_limit=3
clean_requirements_on_remove=True
best=True
skip_if_unavailable=False
nginx内嵌的变量
#
#等于在proxy_pass指令中指定的被代理服务的主机名和端口
$proxy_host
#等于在proxy_pass中指定的服务的端口,或者是其服务的默认端口
$proxy_port
#如果X-Forwarded-For属性未在请求头中,$proxy_add_x_forwarded_for的值就等于$remote_addr;
#如果X-Forwarded-For在请求头中,那$proxy_add_x_forwarded_for的值就等于上一个$proxy_add_x_forwarded_for加上",$remote_addr"。
#例:proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for
$proxy_add_x_forwarded_for
nginx指令
#
rewrite
#
语法: rewrite regex replacement [flag];
context: server,location,if
rewrite指令按照它出现的顺序来执行,可以通过flag来终止执行,如果replacement以"http://,https://或者$scheme"开始,就会直接返回客户端。
flag可以是last、break、redirect、permanent。
redirect是暂时重定向302,permanent是永久重定向301。
rewrite ^(.*)$ https://localhost$1 permanent;
if
#
语法: if(condition) {...}
context: server, localtion
condition包括:
1. 变量名,当变量为空或者为"0"时是false;
2. 变量和字符串通过"="或者"!="来比较;
3. 变量和正则表达式比较【比较符号: ~(大小写敏感)、~*(大小写)不敏感、!~、!~*】,可以通过()来捕获数据。
4. 使用-f 或!-f检测文件书否存在;
5. 使用-d 或!-d检测目录是否存在;
6. 使用-e 或!-e 检测文件、目录、符号连接是否存在;
7. 使用-x 或!-x 检测是否是可执行文件。
if ($http_user_agent ~ MSIE) {
rewrite ^(.*)$ /msie/$1 break;
}
if ($http_cookie ~* "id=([^;]+)(?:;|$)") {
set $id $1;
}
if ($request_method = POST) {
return 405;
}
return
#
语法: return code [text];
return code URL;
return URL;
context: server,location,if
返回特定的状态码,返回444会不发送响应头就关闭连接。
可以指定一个重定向的URL(301,302,303,307,308),或者响应体text(其他的code)。
set
#
语法: set $variable value;
context: server,location,if
给变量赋值,值可以是变量、字符串或者变量和字符串的组合。
2021-08-01
安全通信需要具有下列的特性
-
机密性:仅有发送方和希望的接收方能够理解传输报文的内容。
-
报文完整性:确保其通信内容在传输的过程中未被改变。
-
端点鉴别:发送方和接收方都能证实通信过程所涉及的另一方。
-
运行安全性:确保网络安全。
密码学
#
报文的最初形式称为明文
使用加密算法加密明文,生成的加密报文称为密文,加密算法往往是已知的。
密钥KA是一串数字或者字符,加密算法以密钥和明文报文m为输入,生成的密文为输出。用KA(m)表示。
解密算法使用密钥KB和密文作为输入,通过计算KB(KA(m))得到m
对称密钥密码体制
#
- 凯撒密码
- 将明文报文中的每个字母用字母表中该字母后第k个字母进行替换,允许回绕。
- 缺点:密钥值只有25个
- 单码代替密码
- 使用字母表中的一个字母替换另一个字母。需要给定一个替换表,每个字母都有唯一一个的替换字母。
- 缺点:由于是定向替换,当对密文进行统计分析,并且常见的两三个字母的组合(in、on、the)往往是一起出现,而且如果入侵者知道该密文具有某些可能的东西,破解该密文就不那么困难了。
- 多码代替密码
- 使用多个单码来替换明文的字母,即从多个字母中选一个代替明文中的一个字母,如何选怎呢?需要指定一个次序,比如第一次选择多个字母的第一个,第二次选择多个字母的第二个,第三次选择多个字母的第二个(122),然后按照这个顺序循环多次,直到完成加密。
块密码:要加密的报文被处理为k比特的块。例如。如果k=64,字报文被划分为多个64比特的块,每块被独立加密。
密码块链接:使用块密码时,如果有多个明文块可能是相同的,将会产生相同的密文,解决方式是:发送方为第i个块生成一个随机的k比特数,通过将该数与第i个明文块异或,再将结果加密。该方法有一个问题,就是传输的时候需要两倍的带宽,块密码使用了一种称为密码块链接(Cipher Block Chaining,CBC)的技术,基本思想是只传输一个随机值,剩余的让发送方和接收方计算。流程如下:
-
发送方生成一个随机的k比特串,称为初始向量c(0),发送方以明文方式发送。
-
对于第一个块,发送方计算m(1)⊕c(0),然后加密得到c(1)=Ks(m(1)⊕c(0))。发送方向接收方发送加密块c(1)
-
对于第i个块,发送方根据c(i)=Ks(m(1)⊕c(i-1))向接收方发送第i个密文块。
公开密钥加密
#
世界上任何人都都可以得到公钥(public key)KB+
只有主人知道的私钥(private key)KB-
假如A要和B通信,A首先要取得B的公钥,然后用这个公钥和一个众所周知的加密算法加密他要传给B的报文m,即KB+(m),B接到A的加密报文后,用其私钥和一个众所周知的解密算法解密报文,即计算KB-(KB+(m)),这样就得到了最初的明文m。此外还有KB-(KB+(m)) = KB+(KB-(m))=m。
- RSA
- 生成RSA的公钥和私钥
- 选择两个大素数p和q,p和q越大,破解RSA越困难,执行加密和解密所用的时间也越长。推荐p和q的乘积为1024比特的数量级
- 计算n=pq,z=(p-1)(q-1)
- 选择一个小于n的数e,使得e和z没有公因数,e用来加密
- 求得一个数d使得ed-1可以被z整除,d将用来解密(ed mod z = 1)
- 外界可用的公钥KB+是一对数(n,e),私钥KB-是一对数(n,d)
- 加密和解密过程
- A给B发送一个由整数m表示的比特组合,m<n。加密后的值c=me mod n,将密文c发送给B
- B通过计算获得m,m=cd mod n
- 会话密钥:RSA的指数运算极其耗时,在实际应用中,RSA通常与对称密钥结合使用。首先,A选择一个用于加密数据本身的密钥,这个密钥有时候称为会话密钥Ks。A通过公钥将Ks传输给B,B通过私钥解密后,就获取了Ks,然后使用Ks来传输数据。
报文完整性和数字签名
#
密码散列函数
#
密码散列函数的性质:找到任意两个不同的报文x和y使得H(x)=H(y),在计算上是不可能的。
算法:MD5(128位)、SHA-1(160位)
为了执行报文完整性,A和B需要共享秘密s,这个共享的秘密是个比特串,被称为鉴别密钥。使用这个共享秘密,报文完整性能够执行如下:
- A生成报文m,用s级联m生成m+s,并计算散列H(m+s)。H(m+s)被称为报文鉴别码(Message Authentication Code,MAC)。
- A将MAC附加到报文m上,生成扩展报文(m,H(m+s)),将该扩展报文发送给B。
- B收到扩展报文(m,h),由于知道s,计算出报文鉴别码H(m+s),如果其等于h,则证明一切正常。
数字签名
#
数字签名是B先使用散列函数对报文生成散列,使用私钥对散列进行加密(数字签名)。
A获取到了B的明文报文和已经数字签名的报文摘要,先使用B的公钥解密数字签名,得到一个散列结果,再使用散列函数获取报文的散列,如果这两个散列匹配,则可以确信报文的完整性和发送方。
公钥认证
#
证实一个公钥属于某个特定的实体。
将公钥与特定的实体绑定通常是由认证中心(Certification Authority,CA)完成的。CA证实了一个实体的真实身份;一旦CA验证了某个实体的身份,这个CA会生成一个将其身份和实体的公钥绑定起来的证书。
使TCP连接安全:SSL
#
SSL:Secure Socket Layer,安全套接口协议
TLS:Transport Layer Security,TLS 运输层安全性,是SSL版本3的一个稍加修改的版本
2021-04-26
Object
#
public boolean equals(Object obj) {
return (this == obj);
}
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
String
#
public boolean equals(Object anObject) {
//如果指向的是同一个对象就返回true
if (this == anObject) {
return true;
}
//如果该对象是String的实例
if (anObject instanceof String) {
String anotherString = (String)anObject;
//value存放的是String本身的字符串
int n = value.length;
//如果两个字符串的长度相等
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
//按下标比较两个字符串的字符
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
private int hash; // Default to 0
//...
//当通过new String("xxx")方式创建的字符串,原hash就被赋值给了现在的hash
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
//当hash为0并且字符串长度不为0时,现在的hash是通过循环计算出来的
//由该计算可知道,当字符串一样,它的hashcode就一样
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
2021-04-19
package space.xiaoxiang;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* 通过ssh连接到linux主机,并执行一些shell命令,需要导入JSch这个包
*/
public class App {
private static final String host = "localhost";
private static final int port = 22;
private static final String username = "xiaoxiang";
private static final String password = "asdf";
//连接到linux主机
public Session connect() throws JSchException {
JSch jsch = new JSch();
Session session = jsch.getSession(username, host, port);
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
//开启会话
session.connect();
return session;
}
//执行shell命令
public void execCommand(Session session, String command) throws JSchException, IOException {
ChannelExec channelExec = (ChannelExec) session.openChannel("exec");
InputStream in = channelExec.getInputStream();
channelExec.setCommand(command);
BufferedReader reader = new BufferedReader(new InputStreamReader(channelExec.getInputStream()));
channelExec.connect();
String line;
while((line = reader.readLine()) != null){
System.out.println(line);
}
channelExec.disconnect();
}
public static void main(String[] args) throws IOException, JSchException {
App app = new App();
Session session = app.connect();
app.execCommand(session, "ls -l");
System.out.println("----------");
app.execCommand(session, "df -h");
session.disconnect();
}
}
2021-04-02
- 写mybatis配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--数据库配置文件路径-->
<!--<properties resource="config.properties"/>-->
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--映射文件位置-->
<mappers>
<mapper resource="space/xiaoxiang/dao/XxxxMapper.xml"/>
<!--<package name="space.xiaoxiang.dao"/> 使用package接口的包名必须与xml包名一样-->
</mappers>
</configuration>
- 数据库配置文件config.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/xxxxx?serverTimezone=GMT%2B8&characterEncoding=utf-8
username=xxxx
password=xxxxxxxx
-
写dao层接口和xml文件
- 接口
package space.xiaoxiang.dao;
import java.util.List;
import java.util.Map;
public interface XxxxMapper {
public List<Map<String, Object>> selectAll();
}
- xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="space.xiaoxiang.dao.XxxxtMapper">
<select id="selectAll" resultType="map">
SELECT * FROM xxxx
</select>
</mapper>
-
运行
@Test
public void test() throws IOException {
//数据库配置文件
String databaseConfigFileName = "config.properties";
Properties databaseConfig = new Properties();
databaseConfig.load(Resources.getResourceAsStream(databaseConfigFileName));
//mybatis配置文件
String mybatisConfigFileName = "mybatis-config.xml";
InputStream mybatisConfig = Resources.getResourceAsStream(mybatisConfigFileName);
//一个数据库对应一个SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(mybatisConfig,databaseConfig);
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
XxxxMapper xxxxMapper = sqlSession.getMapper(XxxxMapper.class);
xxxxMapper.selectAll().forEach((map) -> {
xxxxMapper.forEach((k, v) -> {
System.out.println(k + " : " + v);
});
});
}
}
2021-03-27
#zookeeper使用的基本时间单位
tickTime=2000
#存储内存数据库快照的位置
dataDir=/var/lib/zookeeper
#对client端提供服务
clientPort=2181
#超时
initLimit=5
#限制服务器与领导者之间过时的距离
syncLimit=2
#server.X格式列出了组成zookeeper服务的服务器。服务器启动时,它通过在数据目录中查找文件myid*来知道它是哪台服务器。
#2888集群内机器通信使用,3888选举leader使用
server.1=zoo1:2888:3888
server.2=zoo2:2888:3888
server.3=zoo3:2888:3888
zkServer.sh start
:启动zookeeper
zkServer.sh status
:查看当前状态
zkCli.sh -server 127.0.0.1:2181
:连接到zookeeper
ls /
create /zk_test my_data
:创建一个新的znode,并将字符串my_data与该节点关联
get /zk_test
:验证数据是否与znode关联
set /zk_test junk
:来更改与zk_test相关的数据
delete /zk_test
Znode
#
- 组成
- data:Znode存储的数据信息
- stat:包含Znode的各种元数据,如事务ID、版本号、时间戳、大小等
- child:当前节点的子节点引用
- acl:
access control list
访问控制表,表示哪些人或哪些ip可以访问本节点
- Znode是用来存储少量的状态和配置信息,每个节点的数据最大不能超过1MB