文章目录
一、会话保持
在我们做Nginx负载均衡的时候经常会遇到会话保持的问题,为了保证同一用户session会被分配到同一台服务器上,这时就需要会话保持,Nginx会话保持一般有基于ip_hash和基于cookie两种方式
1、基于ip_hash
原理:
ip_hash使用源地址哈希算法,将同一客户端的请求总是发往同一个后端服务器,除非该服务器不可用。
ip_hash语法:
upstream backend {
ip_hash;
server backend1.example.com;
server backend2.example.com;
server backend3.example.com down;
}
ip_hash简单易用,但有如下问题:
- 当后端服务器宕机后,session会话丢失;
- 同一客户端会被转发到同一个后端服务器,可能导致负载失衡;
2、基于cookie
原理:
将用户的session存入cookie里,当用户分配到不同的服务器时,先判断服务器是否存在该用户的session,如果没有就先把cookie里面的sessoin存入该服务器,实现session会话保持。缺点是存入cookie有安全隐患。
使用sticky_cookie_insert
启用会话亲缘关系,这会导致来自同一客户端的请求被传递到一组服务器的同一台服务器。与ip_hash
不同之处在于,它不是基于IP
来判断客户端的,而是基于cookie
来判断。因此可以避免上述ip_hash
中来自同一客户端导致负载失衡的情况。(需要引入第三方模块才能实现,sticky模块)
语法:
upstream backend {
server backend1.example.com;
server backend2.example.com;
sticky_cookie_insert srv_id expires=1h domain=3evip.cn path=/;
}
server {
listen 80;
server_name 3evip.cn;
location / {
proxy_pass http://backend;
}
}
说明:
- expires:设置浏览器中保持cookie的时间
- domain:定义cookie的域
- path:为cookie定义路径
二、防盗链
两个网站 A 和 B, B网站引用了A网站上的图片,这种行为就叫做盗链。 防盗链,就是要防止B引用A的图片。
1、对应模块
ngx_http_referer_module
如何区分哪些是不正常的用户?
HTTP Referer
是Header的一部分,当浏览器向Web服务器发送请求的时候,一般会带上Referer
,告诉服务器我是从哪个页面链接过来的,服务器借此可以获得一些信息用于处理,例如防止未经允许的网站盗链图片、文件等。因此HTTP Referer
头信息是可以通过程序来伪装生成的,所以通过Referer信息防盗链并非100%可靠,但是,它能够限制大部分的盗链情况。
2.、防盗链配置
配置要点:
[root@nginx-server ~]# vim /etc/nginx/nginx.conf
# 日志格式添加"$http_referer"
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# valid_referers 使用方式
Syntax: valid_referers none | blocked | server_names | string ...;
Default: —
Context: server, location
-
valid_referers:允许哪些域名访问。
-
none : 允许没有
http_referer
的请求访问资源; -
blocked : 允许不是
http://
开头的,不带协议的请求访问资源—被防火墙过滤掉的; -
server_names : 只允许指定ip/域名来的请求访问资源(白名单);
3、防盗链实例一
准备两台机器,一台图片网站服务器,一台盗链机器
(1)图片网站服务器
图片网站服务器:上传图片192.168.1.9
[root@nginx-server ~]# cp test.jpg /usr/share/nginx/html/
[root@nginx-server ~]# cd /etc/nginx/conf.d/
[root@nginx-server conf.d]# cp default.conf default.conf.bak
[root@nginx-server conf.d]# mv default.conf nginx.conf
[root@nginx-server conf.d]# vim nginx.conf
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
[root@nginx-server conf.d]# nginx -t
[root@nginx-server conf.d]# systemctl restart nginx
访问:
注:Referer:这个匹配的连接为空 “-”
(2)盗链机器
盗链机器配置:192.168.1.10
[root@nginx-client html]# cat /etc/nginx/conf.d/qf.conf
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html;
}
}
[root@nginx-client ~]# cd /usr/share/nginx/html/
[root@nginx-client html]# cp index.html index.html.bak
[root@nginx-client html]# vim index.html
<html>
<head>
<meta charset="utf-8">
<title>qf.com</title>
</head>
<body style="background-color:red;">
<img src="http://192.168.1.9/test.jpg"/>
</body>
</html>
[root@nginx-client html]# systemctl restart nginx
查看图片网站服务器日志:
注:Referer记录了:连接是1.10这台机器。
(3)图片服务器设置防盗链
在图片服务器操作
[root@nginx-server conf.d]# vim nginx.conf
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
valid_referers none blocked www.jd.com; #允许这些访问
if ($invalid_referer) {
return 403;
}
}
}
[root@nginx-server conf.d]# systemctl restart nginx
测试访问盗链机器:
图片服务器查看日志:
注:上面配置并没有允许192.168.1.10这台机器访问。
4、防盗链实例二
继续在图片服务器上面操作
[root@nginx-server html]# vim /etc/nginx/conf.d/nginx.conf #将原来的删除掉
server {
listen 80;
server_name localhost;
location ~ .*\.(gif|jpg|png|jpeg)$ {
root /usr/share/nginx/html;
valid_referers none blocked *.qf.com 192.168.1.10;
if ($invalid_referer) {
return 403;
}
}
}
因为none允许为空值访问,所以加不加ip都可以访问,如果把none擦除,就不可以了
重载nginx服务
[root@nginx-server ~]# nginx -s reload
在其中一台机器测试:
测试不带http_refer:
[root@nginx-server conf.d]# curl -I "http://192.168.1.9/test.jpg"
HTTP/1.1 200 OK
Server: nginx/1.16.1
Date: Mon, 02 Sep 2019 14:02:56 GMT
Content-Type: image/jpeg
Content-Length: 27961
Last-Modified: Mon, 02 Sep 2019 13:23:12 GMT
Connection: keep-alive
ETag: "5d6d17c0-6d39"
Accept-Ranges: bytes
测试带非法http_refer:
[root@nginx-server conf.d]# curl -e http://www.baidu.com -I "http://192.168.1.9/test.jpg"
HTTP/1.1 403 Forbidden
Server: nginx/1.16.1
Date: Mon, 02 Sep 2019 14:03:48 GMT
Content-Type: text/html
Content-Length: 153
Connection: keep-alive
测试带合法的http_refer:
[root@nginx-server conf.d]# curl -e http://www.qf.com -I "http://192.168.1.9/test.jpg"
HTTP/1.1 200 OK
Server: nginx/1.16.1
Date: Mon, 02 Sep 2019 14:04:52 GMT
Content-Type: image/jpeg
Content-Length: 27961
Last-Modified: Mon, 02 Sep 2019 13:23:12 GMT
Connection: keep-alive
ETag: "5d6d17c0-6d39"
Accept-Ranges: bytes
[root@ansible-server conf.d]# curl -e http://192.168.1.10 -I "http://192.168.1.9/test.jpg"
HTTP/1.1 200 OK
Server: nginx/1.16.1
Date: Mon, 02 Sep 2019 14:05:36 GMT
Content-Type: image/jpeg
Content-Length: 27961
Last-Modified: Mon, 02 Sep 2019 13:23:12 GMT
Connection: keep-alive
ETag: "5d6d17c0-6d39"
Accept-Ranges: bytes
如果用户直接在浏览器输入你的图片地址,那么图片显示正常,因为它符合规则。
在图片服务器查看日志:
三、地址重定向
1、什么是Rewrite
Rewrite又称URL Rewrite,即URL重写,就是把传入Web的请求重定向到其他URL的过程。
2、应用场景
- 伪静态化,是将动态页面显示为静态页面方式的一种技术。理论上,搜索引擎更喜欢静态页面形式的网页,搜索引擎对静态页面的评分一般要高于动态页面。所以,URL Rewrite可以让我们网站的网页更容易被搜索引擎所收录。
- 提高安全性,如果在URL中暴露太多的参数,无疑会造成一定量的信息泄漏,可能会被一些黑客利用,对你的系统造成一定的破坏,所以静态化的URL地址可以给我们带来更高的安全性。
- 美化URL,去除一些后缀名或参数串,让网页的地址看起来尽可能简洁明快,有利于反映访问模块内容。
- 实现地址跳转、协议跳转、端口跳转。
3、Rewrite 相关指令
Nginx Rewrite 相关指令有 if、rewrite、set、return
(1)if 语句
- 应用环境
server,location
语法:
if (condition) { … }
if 可以支持如下条件判断匹配符号
~ 正则匹配 (区分大小写)
~* 正则匹配 (不区分大小写)
!~ 正则不匹配 (区分大小写)
!~* 正则不匹配 (不区分大小写)
-f 和!-f 用来判断是否存在文件
-d 和!-d 用来判断是否存在目录
-e 和!-e 用来判断是否存在文件或目录
-x 和!-x 用来判断文件是否可执行
在匹配过程中可以引用一些Nginx的全局变量
$args 请求中的参数;
$document_root 针对当前请求的根路径设置值;
$host 请求信息中的"Host",如果请求中没有Host行,则等于设置的服务器名;
$limit_rate 对连接速率的限制;
$request_method 请求的方法,比如"GET"、"POST"等;
$remote_addr 客户端地址;
$remote_port 客户端端口号;
$remote_user 客户端用户名,认证用;
$request_filename 当前请求的文件路径名(带网站的主目录/usr/local/nginx/html/images /a.jpg)
$request_uri 当前请求的文件路径名(不带网站的主目录/images/a.jpg)
$query_string 与$args相同;
$scheme 用的协议,比如http或者是https
$server_protocol 请求的协议版本,"HTTP/1.0"或"HTTP/1.1";
$server_addr 服务器地址,如果没有用listen指明服务器地址,使用这个变量将发起一次系统调用以取得地址(造成资源浪费);
$server_name 请求到达的服务器名;
$document_uri 与$uri一样,URI地址;
$server_port 请求到达的服务器端口号;
(2)Rewrite flag
rewrite 指令根据表达式来重定向URI,或者修改字符串。可以应用于server,location, if环境下每行rewrite指令最后跟一个flag标记,支持的flag标记有:
标记 | 作用 |
---|---|
last | 标记在本条 rewrite 规则执行完后,会对其所在的 server { … } 标签重新发起请求,默认为last |
break | 本条规则匹配完成后,终止匹配,不再匹配后面的规则 |
redirect | 返回302临时重定向,浏览器地址会显示跳转后的URL地址 |
permanent | 返回301永久重定向,浏览器地址会显示跳转后URL地址 |
其实
permanent
和redirect
区别就是301和302的区别,301重定向是永久性的,浏览器会把这个跳转规则进行缓存,所以关闭nginx
后仍然会进行跳转,302重定向是临时更改,不会被缓存或保存
(3)Rewrite匹配参考示例
本地解析hosts文件(windows)
192.168.62.153 www.testpm.com
例1:
[root@nginx html]# pwd
/html
[root@nginx html]# ls
a b
[root@nginx html]# cat a/1.html
1.html
[root@nginx html]# cat b/2.html
22
# http://www.testpm.com/a/1.html ==> http://www.testpm.com/b/2.html
server {
listen 80;
server_name www.testpm.com;
location /a {
root /html;
index 1.html index.htm;
rewrite .* /b/2.html permanent;
}
location /b {
root /html;
index 2.html index.htm;
}
}
例2:
[root@mycat html]# pwd
/var/www/html
[root@mycat html]# ls
2018 2019
[root@mycat html]# cat 2018/a/1.html
2018
[root@mycat html]# cat 2019/a/1.html
2019
# http://www.testpm.com/2019/a/1.html ==> http://www.testpm.com/2018/a/1.html
server {
listen 80;
server_name www.testpm.com;
location /2019/a {
root /var/www/html;
index 1.html index.hml;
rewrite ^/2019/(.*)$ /2018/$1 permanent;
}
location /2018/a {
root /var/www/html;
index 1.html index.htl;
}
}
例3:
# http://www.qf.com/a/1.html ==> http://jd.com
location /a {
root /html;
if ($host ~* qf.com ) {
rewrite .* http://jd.com permanent;
}
}
例4:
# http://www.youngfit.com/a/1.html ==> http://jd.com/a/1.html
location /a {
root /html;
if ( $host ~* youngfit.com ){
rewrite .* http://jd.com$request_uri permanent;
}
}
例5: 在访问目录后添加/ (如果目录后已有/,则不加/)
[root@nginx-server c]# pwd
/usr/share/nginx/html/a/b/c
http://www.qf.com/a/b/c--->http://www.qf.com/a/b/c/
# http://www.qf.com/a/b/c
# $1: /a/b/
# $2: c
# http://$host$1$2/
location /a/b/c {
root /usr/share/nginx/html;
index index.html index.hml;
if (-d $request_filename) {
rewrite ^(.*)([^/])$ http://$host$1$2/ permanent;
}
}
例6:
[root@nginx html]# pwd
/usr/share/nginx/html
[root@nginx html]# ls
50x.html index.html index.html.bak1 reg
[root@nginx html]# cat reg/login.html
login
# http://www.qf.com/login/qf.html ==> http://www.qf.com/reg/login.html?user=qf
location /login {
root /usr/share/nginx/html;
rewrite ^/login/(.*)\.html$ http://$host/reg/login.html?user=$1;
}
location /reg {
root /usr/share/nginx/html;
index login.html;
}
例7:
[root@nginx-server 33]# pwd
/html/qf/11/22/33
[root@nginx-server 33]# cat 1.html
hello nginx
#http://www.qf.com/qf/11-22-33/1.html ==> http://www.qf.com/qf/11/22/33/1.html
location /qf {
rewrite ^/qf/([0-9]+)-([0-9]+)-([0-9]+)(.*)$ /qf/$1/$2/$3$4 permanent;
}
location /qf/11/22/33 {
root /html;
index 1.html;
}
(4)set 指令
set 指令是用于定义一个变量,并且赋值
应用环境:
server,location,if
应用示例
例8:
#http://alice.testpm.com ==> http://www.testpm.com/alice
#http://jack.testpm.com ==> http://www.testpm.com/jack
[root@nginx-server conf.d]# cd /usr/share/nginx/html/
[root@nginx-server html]# mkdir jack alice
[root@nginx-server html]# echo "jack.." >> jack/index.html
[root@nginx-server html]# echo "alice.." >> alice/index.html
本地解析域名host文件
192.168.62.153 www.testpm.com
192.168.62.153 alice.testpm.com
192.168.62.153 jack.testpm.com
编辑配置文件:
server {
listen 80;
server_name www.testpm.com;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
if ( $host ~* ^www.testpm.com$) {
break;
}
if ( $host ~* "^(.*)\.testpm\.com$" ) {
set $user $1;
rewrite .* http://www.testpm.com/$user permanent;
}
}
location /jack {
root /usr/share/nginx/html;
index index.html index.hml;
}
location /alice {
root /usr/share/nginx/html;
index index.html index.hml;
}
}
(5)return 指令
return 指令用于返回状态码给客户端
server,location,if
应用示例:
例9:如果访问的.sh结尾的文件则返回403操作拒绝错误
http://www.testpm.com/1.sh 返回403
server {
listen 80;
server_name www.testpm.com;
#access_log /var/log/nginx/http_access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
location ~* \.sh$ {
return 403;
}
}
例10:80 ======> 443:80转443端口
server {
listen 80;
server_name www.testpm.cn;
access_log /var/log/nginx/http_access.log main;
return 301 https://www.testpm.cn$request_uri;
}
server {
listen 443 ssl;
server_name www.testpm.cn;
access_log /var/log/nginx/https_access.log main;
#ssl on;
ssl_certificate /etc/nginx/cert/2447549_www.testpm.cn.pem;
ssl_certificate_key /etc/nginx/cert/2447549_www.testpm.cn.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
ssl_prefer_server_ciphers on;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
[root@nginx-server ~]# curl -I http://www.testpm.cn
HTTP/1.1 301 Moved Permanently
Server: nginx/1.16.0
Date: Wed, 03 Jul 2019 13:52:30 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: https://www.testpm.cn/
4、location
语法
在server块中使用,如
server {
location 表达式 {
}
}
location表达式类型
符号 | 描述 |
---|---|
~ | 表示执行一个正则匹配,区分大小写 |
~* | 表示执行一个正则匹配,不区分大小写 |
^~ | 表示普通字符匹配。使用前缀匹配。如果匹配成功,则不再匹配其他location。 |
= | 进行普通字符精确匹配。也就是完全匹配。 |
优先级
- 等号类型(=)的优先级最高。一旦匹配成功,则不再查找其他匹配项。
- ^~类型表达式。一旦匹配成功,则不再查找其他匹配项。
- 正则表达式类型(~ ~*)的优先级次之。如果有多个location的正则能匹配的话,则使用正则表达式最长的那个。
- 常规字符串匹配类型。按前缀匹配。
例子- 假地址掩饰真地址
server {
# 用 xxoo_admin 来掩饰 admin
location / {
# 使用break拿一旦匹配成功则忽略后续location
rewrite /xxoo_admin /admin break;
}
# 访问真实地址直接报没权限
location /admin {
return 403;
}
}
四、知识扩展
1、HTTP Server和Application Server的区别和联系
Apache/Nginx是静态服务器(HTTP Server);
-
Nginx优点:负载均衡、反向代理、处理静态文件优势。nginx处理静态请求的速度高于apache;
-
Apache优点:相对于Tomcat服务器来说处理静态文件是它的优势,速度快。Apache是静态解析,适合静态HTML、图片等。
-
HTTP Server 关心的是 HTTP 协议层面的传输和访问控制,所以在 Apache/Nginx 上你可以看到代理、负载均衡等功能
-
HTTP Server(Nginx/Apache)常用做静态内容服务和代理服务器,将外来请求转发给后面的应用服务(tomcat,jboss,jetty等)。
应用服务器(tomcat/jboss/jetty)是动态服务器(Application Server):
-
应用服务器Application Server,则是一个应用执行的容器。它首先需要支持开发语言的 Runtime(对于 Tomcat 来说,就是 Java,若是Ruby/Python 等其他语言开发的应用也无法直接运行在 Tomcat 上)。
-
但是事无绝对,为了方便,应用服务器(如tomcat)往往也会集成 HTTP Server 的功能,nginx也可以通过模块开发来提供应用功能,只是不如专业的 HTTP Server 那么强大,所以应用服务器往往是运行在 HTTP Server 的背后,执行应用,将动态的内容转化为静态的内容之后,通过 HTTP Server 返回到客户端。
2、集群软硬件
-
常用开源集群软件有:lvs,keepalived,haproxy,nginx,apache,heartbeat
-
常用商业集群硬件有:F5, Netscaler,Radware,A10等