ip限流

1.限流的思路

使用redis 功能,了解Redis中的key(IP+URL),从而记录了某个IP访问的某个接口,value存的是访问的次数,加上一个过期时间就是在注解赋值的值。

  • 限流的思路就是去监控这个ip,使用拦截器来进行拦截,
  • 通过 ip的作为key,访问次数为value的方式对某一用户的某一请求进行唯一的标识。
  • 每次访问的时候判断key 是否存在,是否count超过了限制的访问次数、
  • 若超出访问的限制,则应response放回msg:请求过于频繁,给前端予以展示。

自定义注解

@interface 就是要做自定义注解了
@Retention(RetentionPolicy.RUNTIME) 运行时使用
@Target(可以查看是放在那个上面的,是方法上面还是类上面)
int seconds();
int maxCount();
boolean needLogin() default true;

2.拦截器

使用的是handlerInterceptor

HandlerInterceptor

  1. preHandle 方法是处理拦截器拦截使用的,在controller处理之前就开始进行调用了,springmvc中夫人Interceptor是链式调用的,所有的prehandle 方法都会在controller方法调用之前调用。 prehandle 方法,在请求发生前执行的。
  2. posthandle 返回true 调用的前提:prehandle 返回TRUE
    调用前提:prehandle 返回TRUE
    调用时间:controller方法处理完。
    执行顺序:链式Intercepter情况下,按照声明的顺序倒着执行。
  3. afterCompletion
    调用的前提:prehandle返回TRUE
    调用时间:DispatcherServlet进行视图渲染之后多用于清理资源。

首先是拦截器

小于最大的操作,是不是加1操作

首次进入

此时访问次数小于最大的次数

此时访问的次数大于最大的次数,

之后就开始写controller+6

使用自定义注解

@GetMapping
@AccessLimit(seconds = 3,maxCount = 10)
public String accessLimit( ){

拿到限流类的注解的参数,拿到方法的注解,就可以使用的最大的次数。

拦截路径:

全面的使用注解:InteceptorConfig WebMvcConfigurer 增加一个拦截器

配置Redis 6379

反向代理的情况下使用的request.getRemoteAddr( ),获取的Ip地址就是Nginx所在服务器的ip地址,而不知客户端的ip。

利用X-forwarded-for 伪造客户端

在web开发中,获取客户端ip地址,为了防止刷票,需要限制每个IP只能投票一次。

获取客户端IP

在Java中获取客户端最直接的方式使用request.getRemoteAddr( ) 这种

HTTP协议是基于TCP协议,由于request,getRemoteAddr( )获取到的就是TCP层直接连接的客户端的IP,对于web服务器来说直接连接的服务器实际上为Nginx,也就是tcp层拿不到真实客户端的IP

为了解决上面的问题,很多的HTTP代理会在HTTP协议头中添加X-forward-for,用来跟踪请求的来源。

X-Forwarded-For:client1,proxy1,proxy2

包含多个IP地址,每个值通过逗号+空格分开,最左边(client1)是最原始客户端的地址,中间如果有多层代理,每一层代理都会将连接他的客户端IP,追加在X-Forwarded-For右边

获取真实的IP地址的话,首先从HTTP头中获取X-Forwarded-For,如果X-Forwarded-For 存在就按逗号分隔取最左边的第一个IP地址,不存在直接通过request.getRemoteAdrr();

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-

haproxy redis根据ip分流_java


D2i1HViI-1650442761725)(ip%E9%99%90%E6%B5%81%E5%8A%9F%E8%83%BD%E7%9A%84%E5%AE%9E%E7%8E%B0/image-20220417202935603.png)]

伪造X-Forwarded-For

因为在客户端和服务端进行通信的时候,我们需要进行三次握手

X-Forwarded-For 介绍

X-Forwarded-For 是一个 HTTP 扩展头部,用来表示HTTP请求端真实 IP,HTTP/1.1 协议并没有对它的定义,但现如今X-Forwarded-For已被各大 HTTP 代理、负载均衡等转发服务广泛使用。

一般的客户端发送HTTP请求是没有X-Forwarded-For,当请求到达第一个代理服务器时,代理服务器上会加上X-Forwarded-For,并将值设为客户端的IP地址(也就是左边的第一个值),后面如果还有很多代理,会将IP追加到X-Forwarded-For最右边,最终请求到达web应用服务器,应用通过X-Forwarded-For头取左边第一个IP为真实IP,如果客户端在发起请求的时候,请求头上带上一个伪造的X-Forwarded-For,由于后续的每层代理只会追加不会覆盖,那么最终到达应用服务器时,获取左边的第一个IP地址将会是客户端伪造的IP。也就是Java代码getClientIp()方法很有可能是伪造的IP地址,如果投票系统使用这样进行限制的话,很容易被刷票。

伪造X-Forwarded-For,使用postman来进行伪造就可以了

如果让Nginx支持X-Forwarded-For

proxy_ser_header X-Forwarded-For $proxy_add_x_forwarded_for

如果用户在请求时候伪造的话,那么会出现上面案例的client1前面,出现伪造的ip:

X-Forwarded-For: 伪造ip1, 伪造ip2, client_ip, proxy1_ip, proxy2_ip

防范:

  • 直接在对外的Nginx反向代理的服务器上配置

既然我们能够直接获得真实的客户端 IP,那么我们为什么还要获得 X-Forwarded-For 呢?原因在于如果配置了多层的代理,那么这个 X-Real-IP 将会是上一层代理的真实 IP。

我们直接获得 X-Forwarded-For 将会有 IP 被伪造的风险,而使用 X-Real-IP 将会无法获得真实的 IP 地址。我们将两者的优势进行结合,便可防止客户端 IP 伪造。

proxy_set_header X-Real-IP $remote_addr
proxy_set_header X-Forwarded-For $remote_addr
location / {
                proxy_set_header        X-Real-IP       $remote_addr;
                proxy_set_header        Host            $host;
                proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
                ...
            }
 RequestAttributes ra = RequestContextHolder.getRequestAttributes();
    ServletRequestAttributes sra = (ServletRequestAttributes) ra;
    HttpServletRequest request = sra.getRequest();
    //客户端真实IP
    String ip = request.getHeader("X-Real-IP");

haproxy redis根据ip分流_IP_02proxy_add_x_forwarded_for,KaTeX parse error: Double subscript at position 12: proxy_add_x_̲forwarded_for会在…remote_addr是获取的是直接TCP连接的客户端IP。(类似于在JAVA中的request.getRemoteAddr(),是无法伪造的。即使客户端伪造也会被覆盖掉,而不是追加。

如果有多层代理,那么只要直接对外访问的Nginx上配置X-Forwarded-For为$remote_addr,内部的还是跟之前一样,不然内部的Nginx又会覆盖掉客户端的真实IP

  • tomcat的源码的方法

遍历头中的IP地址,从右向左遍历,遍历可以通过正则表达式去除内网IP和代理服务器本身的IP,拿到的第一个非移除会使一个可信任的ip

RemoteIpValve可以替换Servlet API中request.getRemoteAddr()方法的实现,让request.getRemoteAddr()方法从X-Forwarded-For头中获取IP地址。也就是在业务代码中不需要再自己实现类似于上面的getClientIp()方法来从X-Forwarded-For中获取IP,而是直接使用request.getRemoteAddr()方法。想要使用RemoteIpValve,仅需要在Tomcat配置文件server.xml中Host元素内末尾加上:

<Valve className="org.apache.catalina.valves.RemoteIpValve" ... />

RemoteIpValve有一套防止伪造X-Forwarded-For的机制,实现思路:遍历X-Forwarded-For头中的IP地址,和方法一不同的是,不是直接取左边第一个IP,而是从右向左遍历。遍历时可以根据正则表达式剔除掉内网IP和已知的代理服务器本身的IP(例如192.168开头的IP),那么拿到的第一个非剔除IP就会是一个可信任的客户端IP。这种方法的巧妙之处在于,即使伪造X-Forwarded-For,那么请求到达应用服务器时,伪造的IP也会在X-Forwarded-For值的左边,真实的IP为放到右边的某个位置,从右向左遍历就可以避免取到这些伪造的IP地址。

.168开头的IP),那么拿到的第一个非剔除IP就会是一个可信任的客户端IP。这种方法的巧妙之处在于,即使伪造X-Forwarded-For,那么请求到达应用服务器时,伪造的IP也会在X-Forwarded-For值的左边,真实的IP为放到右边的某个位置,从右向左遍历就可以避免取到这些伪造的IP地址。