springcloud5-服务网关zuul及gateway

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
EMR Serverless StarRocks,5000CU*H 48000GB*H
网络型负载均衡 NLB,每月750个小时 15LCU
简介: springcloud5-服务网关zuul及gateway

1,课程回顾
2,本章重点

zuul(zuul1,zuul2)

gateway

3,具体内容

3.1 zuul

3.1.1 zuul简介

https://www.springcloud.cc/

https://github.com/Netflix/zuul/wiki

https://www.springcloud.cc/spring-cloud-greenwich.html#_router_and_filter_zuul

Zuul 是从设备和网站到 Netflix 流媒体应用程序后端的所有请求的前门。作为边缘服务应用程序,Zuul 旨在支持动态路由、监控、弹性和安全性。路由是微服务架构不可或缺的一部分。例如,/可能被映射到您的web应用程序,/api/users被映射到用户服务,/api/shop被映射到商店服务。 Zuul是Netflix的基于JVM的路由器和服务器端负载平衡器。

3.1.1 zuul 的主要作用(为什么使用)

Netflix API 流量的数量和多样性有时会导致生产问题在没有警告的情况下迅速出现。我们需要一个允许我们快速改变行为以应对这些情况的系统。

Zuul 使用一系列不同类型的过滤器,使我们能够快速灵活地将功能应用到我们的边缘服务。这些过滤器帮助我们执行以下功能:

身份验证和安全性 - 确定每个资源的身份验证要求并拒绝不满足要求的请求。

洞察力和监控 - 在边缘跟踪有意义的数据和统计数据,以便为我们提供准确的生产视图。

动态路由 - 根据需要将请求动态路由到不同的后端集群。

压力测试 - 逐渐增加集群的流量以衡量性能。

减载 - 为每种类型的请求分配容量并丢弃超出限制的请求。

静态响应处理 - 直接在边缘构建一些响应,而不是将它们转发到内部集群

3.1.2 实现过程(参考项目 zuul)

1)引入jar:

<!--zuul jar-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<!--nacos客户端jar-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>   

2) 启动类:

@SpringBootApplication

@EnableZuulProxy //开启zuul的代理功能

3)配置application.yml

server:
  #端口号配置
  port: 14151
spring:
  cloud:
    nacos:
      discovery:
        #注册地址
        server-addr: localhost:8848
  application:
    #注册服务名称
    name: zuulService
zuul:
  host:
    #总并发连接配置  默认值为200
    max-total-connections: 300
    #每个路由并发连接配置  默认值为20
    max-per-route-connections: 30
    #连接超时时长  默认为2000毫秒
    connect-timeout-millis:  5000
  #路由配置
  routes:
    #自定义路由名称
    order:
      #服务的唯一识别  一定要和想访问的服务注册到注册中心的名称一致
      serviceId: orderService
      #请求该服务的路径配置  **不确定,可以根据地址栏请求,动态路由
      path: /os/**
    goods:
      #服务的唯一识别  一定要和想访问的服务注册到注册中心的名称一致
      serviceId: goodsService
      #请求该服务的路径配置  **不确定,可以根据地址栏请求,动态路由
      path: /gs/**
    sns:
      #服务的唯一识别  一定要和想访问的服务注册到注册中心的名称一致
      serviceId: snsService
      #请求该服务的路径配置  **不确定,可以根据地址栏请求,动态路由

4)路由测试:

http://localhost:14151/ss/comment/selectOne/1?a=1&b=22&c=333nnd33

http://localhost:14151/gs/product/selectAll?current=1&size=2

5)过滤器实现:

jar(增加):

<!--springboot web包  过滤器需要-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>   

filter方法详解:

/**

  • 过滤类型 定义当前过滤器在什么起用
  • @return
    */
@Override
public String filterType() {
    //pre  在执行业务之前执行
    //route  在执行业务方法时执行
    //error  在执行业务时出现异常执行
    //post  在正常执行业务后或者出现异常后 执行
    return "pre";
}
/**
• 多个过滤器同时执行时,通过该方法确定先后顺序 返回值越小,执行优先级越高
• @return
/
@Override
public int filterOrder() {
return 3;
}
/*
• 是否让该过滤器生效 返回false不生效
• @return
/
@Override
public boolean shouldFilter() {
return true;
}
/*
• 开发过滤功能
• @return
• @throws ZuulException
*/
@Override
public Object run() throws ZuulException {
}


上下文对象:

com.netflix.zuul.context.RequestContext
编写IP过滤过滤器:          
package com.aaa.zuul.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.http.HttpStatus;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * @ fileName:IllegalIPFilter
 * @ description:
 * @ author:zhz
 * @ createTime:2022/2/21 20:37
 * @ version:1.0.0
 */
@Component
public class IllegalIPFilter extends ZuulFilter {
    @Value("${illegal_ip}")
    private String illegalIp;
    @Override
    public String filterType() {
        return "pre";
    }
    @Override
    public int filterOrder() {
        return 0;
    }
    @Override
    public boolean shouldFilter() {
        return true;
    }
    @Override
    public Object run() throws ZuulException {
        //具体过滤器业务代码
        System.out.println("进入了IP过滤器。。。。。");
        //获取zuul中提供的请求上下文对象
        RequestContext requestContext = RequestContext.getCurrentContext();
        //获取request
        HttpServletRequest request =requestContext.getRequest();
        //获取response对象
        HttpServletResponse response = requestContext.getResponse();
        //获取请求用户的IP地址
        String remoteAddr = request.getRemoteAddr();
        System.out.println("请求IP为:"+remoteAddr);
        System.out.println(illegalIp+"............");
        //判断IP地址是否合法   192.168.1.120
        //illegalIp=192.168.1.120,192.168.1.110,192.168.1.134,192.168.1.143
        if(illegalIp.contains(remoteAddr)){
            //添加响应方法
               response.setCharacterEncoding("gbk");
            try {
                //阻止程序继续运行
                requestContext.setSendZuulResponse(false);
                //response.sendError(HttpStatus.SC_FORBIDDEN,"禁止访问!");
                response.getWriter().println("IP非法,禁止访问!!!");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}
编写脏字过滤器:
package com.aaa.zs.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
/**
 * @ fileName:IllegalCharFilter
 * @ description:
 * @ author:zhz
 * @ createTime:2022/2/22 9:43
 * @ version:1.0.0
 */
@Component
public class IllegalCharFilter extends ZuulFilter {
    @Value("${illegal_char}")
    private String illegalChar;
    @Override
    public String filterType() {
        return "pre";
    }
    @Override
    public int filterOrder() {
        return 4;
    }
    @Override
    public boolean shouldFilter() {
        return true;
    }
    @Override
    public Object run() throws ZuulException {
        System.out.println("非法字符过滤器。。。。。。。。。。。。");
        //实例化zuul封装请求上下文对象
        RequestContext currentContext = RequestContext.getCurrentContext();
        //通过上下问对象获取request,response
        HttpServletRequest request = currentContext.getRequest();
        HttpServletResponse response = currentContext.getResponse();
        //获取请求的所有参数 //?a=1&b=2&c=3&aa=11   parameterNames [a,b,c,aa]
        Enumeration<String> parameterNames = request.getParameterNames();
        //根据逗号分割 非法字符串 [tmd,nnd,nm,yyds]
        String[] illegalCharAarray = illegalChar.split(",");
        //循环遍历所有参数名称
        while(parameterNames.hasMoreElements()){
            //第1次获取a  第2次获取b  ...
            String parameterName = parameterNames.nextElement();
            //根据名称获取参数值 第1次获取1  第2次获取2  ...
            String parameterValue = request.getParameter(parameterName);
            for (String illegalChar : illegalCharAarray) {
                 if(parameterValue.contains(illegalChar)){
                     //直接阻止程序运行
                     currentContext.setSendZuulResponse(false);
                     //回显错误
                     response.setCharacterEncoding("GBK");
                     try {
                         response.getWriter().println("请求中含有非法参数,禁止访问");
                     } catch (IOException e) {
                         e.printStackTrace();
                     }
                 }
            }
        }
        return null;
    }
}

编写单点登录过滤器(单点登录用到)

跨域过滤(参考3.4 方法2)

package com.aaa.zs.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * @fileName:CrossDomainFilter
 * @description:后台跨域过滤器
 * @author:zz
 * @createTime:2020/11/6 16:20
 * @version:1.0.0
 */
@Component
public class CrossDomainFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return "pre";
    }
    @Override
    public int filterOrder() {
        return 0;
    }
    @Override
    public boolean shouldFilter() {
        return true;
    }
    @Override
    public Object run() throws ZuulException {
        // 获取request对象
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        HttpServletResponse response = ctx.getResponse();
        // 这些是对请求头的匹配,网上有很多解释
        response.setHeader("Access-Control-Allow-Origin",request.getHeader("origin"));
        response.setHeader("Access-Control-Allow-Credentials","true");
        response.setHeader("Access-Control-Allow-Methods","GET, HEAD, POST, PUT, DELETE, OPTIONS, PATCH");
        response.setHeader("Access-Control-Allow-Headers","authorization, content-type");
        response.setHeader("Access-Control-Expose-Headers","X-forwared-port, X-forwarded-host");
        response.setHeader("Vary","Origin,Access-Control-Request-Method,Access-Control-Request-Headers");
        // 跨域请求一共会进行两次请求 先发送options 是否可以请求
        // 调试可发现一共拦截两次 第一次请求为options,第二次为正常的请求 eg:get请求
        //  equalsIgnoreCase 忽略大小写
        if ("OPTIONS".equalsIgnoreCase(request.getMethod())){
            ctx.setSendZuulResponse(false); //验证请求不进行路由
            ctx.setResponseStatusCode(HttpStatus.OK.value());//返回验证成功的状态码
            ctx.set("isSuccess", true);
            return null;
        }
        // 第二次请求(非验证,eg:get请求不会进到上面的if) 会走到这里往下进行
        // 不需要token认证
        ctx.setSendZuulResponse(true); //对请求进行路由
        ctx.setResponseStatusCode(HttpStatus.OK.value());
        ctx.set("isSuccess", true);
        return null;
    }
    public static void main(String[] args) {
        System.out.println("a".equals("A"));
        System.out.println("a".equalsIgnoreCase("A"));
    }
}

测试过滤器的优先级,测试过滤器的启用和禁用等功能

3.2 gateway

https://spring.io/projects/spring-cloud-gateway#overview

https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/

3.3.1 概念

该项目提供了一个用于在Spring WebFlux之上构建API网关的库。Spring Cloud Gateway旨在提供一种简单而有效的方法来路由到API,并为它们提供跨领域的关注点,例如:安全性,监视/指标和弹性。

主要功能: 路由功能route 断言功能predicate 过滤功能filter

webFlux官网文档:

https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html

3.3.2 特点

建立在Spring Framework 5,Project Reactor和Spring Boot 2.0之上

能够匹配任何请求属性上的路由。

谓词和过滤器特定于路由。

断路器集成。

Spring Cloud DiscoveryClient集成

易于编写的谓词和过滤器

请求速率限制(https://www.cnblogs.com/forezp/p/10140316.html)

路径改写

3.3.3 zuul和gateway对比(面试题)

相同点:

1、底层都是servlet

2、两者均是web网关,处理的是http请求

不同点:

1,getway的性能会远高于zuul1

getway和zuul 在zuul没有进行参数调优的时候,getway的性能会远高于zuul。在空负载的时候,SpringCloud Gateway比zuul 1 性能高50%左右,在模拟处理50ms业务后,,SpringCloud Gateway比zuul 1 性能高9倍左右。

zuul优化参数参考如下:

在实际生产使用中,zuul 1虽然使用的是同步io,但是可以通过参数优化提高性能理论上可以达到极限性能,而springcloud gateway使用的是异步io,不需优化既可以达到接近极限的性能。

Zuul 1.x,是一个基于阻塞io的API Gateway。Zuul已经发布了Zuul 2.x,基于Netty,也是非阻塞的,支持长连接,但Spring Cloud暂时还没有整合计划。

SpringCloud GetWay 是基于webFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty(异步非阻塞)。

2、是否支持异步

zuul1 仅支持同步

gateway支持异步。理论上gateway则更适合于提高系统吞吐量(但不一定能有更好的性能),最终性能还需要通过严密的压测来决定

3.3.4 工作原理

客户端向Spring Cloud Gateway发出请求。如果网关处理程序映射确定请求与路由匹配,则将其发送到网关Web处理程序。该处理程序通过特定于请求的过滤器链运行请求。筛选器由虚线分隔的原因是,筛选器可以在发送代理请求之前和之后运行逻辑。所有“前置”过滤器逻辑均被执行。然后发出代理请求。发出代理请求后,将运行“后”过滤器逻辑。              

3.3.5 具体使用

1)创建项目,引入jar包

<!--gateway jar包-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--nacos 服务发现 客户端包-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--fastjson 包 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
</dependency>   

2) 创建配置

https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-host-route-predicate-factory

server:
  port: 14152  #当前服务端口号
spring:
  application:
    name: gatewayService  #当前服务名称
  cloud:
    gateway:
      routes: #表示多个路由
        - id: order_route  # 每个网关的唯一标识,不可以重复
          name: order-service #名称,可以省略配置
          #uri: http://localhost:14121 #匹配后提供服务的路由地址
          uri: lb://orderService  #匹配后提供服务的路由地址,必须和注册中心注册名称一致 lb 支持负载均衡  注册服务是多个
          predicates: # 断言,满足条件,就执行
            - Path=/order/**
        - id: goods_route  # 每个网关的唯一标识,不可以重复
          uri: lb://goodsService  #配置被访问服务的名称,必须和注册中心注册名称一致
          predicates: # 断言,满足条件,就执行
            - Path=/product/**,/productType/**  #多个路径可以用,隔开
            - Method=POST,GET,PUT,DELETE  #请求方式是 POST,GET,PUT或者DELETE
            - Query=id,\d+  #要有参数id并且要求id为整数
            - Before=2023-02-22T11:25:40.666+08:00[Asia/Shanghai] #匹配这个时间之前的请求  UTC时间,也就是国际统一时间  +8:00 东八区
            - After=2021-02-21T12:15:30.666+08:00[Asia/Shanghai]  #匹配这个时间之后的请求
            - Between=2021-02-21T12:15:30.666+08:00[Asia/Shanghai],2023-02-21T12:15:30.666+08:00[Asia/Shanghai]
        - id: sns_route  # 每个网关的唯一标识,不可以重复
          name: sns-service #名称,可以省略配置
          #uri: http://localhost:14121 #匹配后提供服务的路由地址
          uri: lb://snsService  #匹配后提供服务的路由地址,必须和注册中心注册名称一致
          predicates: # 断言,满足条件,就执行
            - Path=/comment/**
    nacos:
      discovery:
        server-addr: localhost:8848/  #注册中心地址  gateway必须注册,才能发现其他服务
#自定义非法IP
illegal_ip: 192.168.41.174,192.168.1.110,192.168.1.134,192.168.1.143

3) 创建启动类

package com.aaa.gws;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
 * @ fileName:GatewayApp
 * @ description:
 * @ author:zhz
 * @ createTime:2022/2/22 11:14
 * @ version:1.0.0
 */
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApp {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApp.class,args);
    }
}

4) 启动项目,路由测试

先启动order服务,再启动gateway

http://localhost:14152/productType/selectOne/1?id=1

http://localhost:18888/order/selectOne?id=1

5)过滤器配置:

添加配置(application.yml):

#配置非法IP

illegal_ip: 192.168.1.120,192.168.1.110,192.168.1.134,192.168.1.143

#配置非法字符 BAT=baidu alibaba tencent

illegal_char: sb,nnd,tmd

#白名单配置

white_list: /order/selectOne,userLogin,/order/selectAll,memberLogin

非法字符滤器:

使用gateway过滤功能,过滤所有请求,获取所有请求中所有参数,如果请求参数中含有系统配置非法字符,直接不让提示错误。

package com.aaa.gs.filter;
import com.aaa.common.util.ResultStatus;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
 * @ fileName:IllegalIPFilter
 * @ description: 非法字符过滤器
 * @ author:zhz
 * @ createTime:2022/6/27 15:58
 * @ version:1.0.0
 */
@Component
public class IllegalCharacterFilter implements GlobalFilter, Ordered {
    //基本属性依赖注入配置的值
    @Value("${illegal_char}")
    private String illegalChar;
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("经过了非法字符过滤器。。。。。。。。。。。。。。。。。。");
        //获取request对象
        ServerHttpRequest request = exchange.getRequest();
        //获取response对象
        ServerHttpResponse response = exchange.getResponse();
        //获取所有请求参数的值,判断是否含有非法字符
          //http://192.168.28.173:14966/order/queryById?id=3&a=1&b=2&c=3&c=4tmd4
          //获取请求的所有参数及值  id=3&a=1&b=2&c=3&c=4tmd4  checkbox =  ["体育","读书"]
        MultiValueMap<String, String> queryParamsMap = request.getQueryParams();
          // MultiValueMap是Map的子类  遍历queryParamsMap [{key=id value=[3]},{key=a value=[1]}....]
        Set<Map.Entry<String, List<String>>> queryParamsMapEntries = queryParamsMap.entrySet();
          //获取非法字符并分割为数组 [tmd,nnd,fuck,falungong,guomindang]
        String[] illegalCharArray = illegalChar.split(",");
          //定义标识符,判断是否含有非法字符
        boolean isContainsIllegalChar = false;
          //循环遍历
        for (Map.Entry<String, List<String>> queryParamsMapEntry : queryParamsMapEntries) {
            String key = queryParamsMapEntry.getKey();
            //第一次 id  第二次  a ....
            System.out.println("key:"+key);
            //第一次 3  第二次  1 .... 第四次 4tmd4
            //  "tmd,nnd,fuck,falungong,guomindang".contains(4tmd4)  判断不对
            String value = queryParamsMapEntry.getValue().get(0);
            System.out.println("value:"+value);
            //循环遍历非法数组
            for (String illegalChar : illegalCharArray) {
                //第1次 tmd 第2次 nnd .......
                //判断是否含有
                if(value.contains(illegalChar)){
                    //定义返回map对象
                    Map map = new HashMap();
                    map.put("code", ResultStatus.ILLEGAL_CHAR_ERROR.getReturnCode());
                    map.put("msg", ResultStatus.ILLEGAL_CHAR_ERROR.getReturnMsg());
                    byte[] bytesMap = null;
                    try {
                        //把map转换为字节数组
                        bytesMap = JSON.toJSONString(map).getBytes("GBK");
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                    //spring 提供数据缓冲类  使用response.bufferFactory()中提供的wrap方法,把字节数组转换为DataBuffer
                    DataBuffer dataBuffer = response.bufferFactory().wrap(bytesMap);
                    //writeWith 向响应对象中写入影响数据  需要的是Publisher接口的实现类   Mono就是该接口的实现类
                    return response.writeWith(Mono.just(dataBuffer));
                    /*isContainsIllegalChar = true;
                    break;*/
                }
            }
        }
        //判断标识符
       /* if(isContainsIllegalChar){
            //定义返回map对象
            Map map = new HashMap();
            map.put("code", ResultStatus.ILLEGAL_CHAR_ERROR.getReturnCode());
            map.put("msg", ResultStatus.ILLEGAL_CHAR_ERROR.getReturnMsg());
            byte[] bytesMap = null;
            try {
                //把map转换为字节数组
                bytesMap = JSON.toJSONString(map).getBytes("GBK");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            //spring 提供数据缓冲类  使用response.bufferFactory()中提供的wrap方法,把字节数组转换为DataBuffer
            DataBuffer dataBuffer = response.bufferFactory().wrap(bytesMap);
            //writeWith 向响应对象中写入影响数据  需要的是Publisher接口的实现类   Mono就是该接口的实现类
            return response.writeWith(Mono.just(dataBuffer));
        }*/
        //如果不是,让程序继续运行
        return chain.filter(exchange);
    }
    @Override
    public int getOrder() {
        return 5;
    }
}

非法IP过滤:

拦截所有客户请求,获取请求IP地址,判断地址是否在非法IP列表中,如果在,直接拦截,提示错误,不在放行。

```java
package com.aaa.gs.filter;
import com.aaa.common.util.ResultStatus;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
/**
 * @ fileName:IllegalIPFilter
 * @ description: 非法IP过滤器
 * @ author:zhz
 * @ createTime:2022/6/27 15:58
 * @ version:1.0.0
 */
@Component
public class IllegalIPFilter implements GlobalFilter, Ordered {
    //基本属性依赖注入配置的值
    @Value("${illegal_ip}")
    private String illegalIp;
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("经过了非法IP过滤器。。。。。。。。。。。。。。。。。。");
        //获取request对象
        ServerHttpRequest request = exchange.getRequest();
        //获取response对象
        ServerHttpResponse response = exchange.getResponse();
        //IP过滤功能 如果是非法IP,直接拦截,提示错误
           //获取客户请求的IP地址
        InetSocketAddress remoteAddress = request.getRemoteAddress();
        //String hostName = remoteAddress.getHostName();
        InetAddress address = remoteAddress.getAddress();
        //System.out.println("主机名称----------------:"+hostName);
        String hostAddress = address.getHostAddress();
        System.out.println("IP地址----------------:"+hostAddress);
        //判断获取到IP是否是192.168.28.70,192.168.28.133
        //if(hostAddress.equals("192.168.28.173")||hostAddress.equals("192.168.28.70")||hostAddress.equals("192.168.28.133")||hostAddress.equals("192.168.28.47")){
        // illegalIp=192.168.28.173,192.168.28.70,192.168.28.133,192.168.28.47,192.168.28.238
        // "192.168.28.173,192.168.28.70,192.168.28.133,192.168.28.47,192.168.28.238".contains("192.168.28.173") 为true
        if(illegalIp.contains(hostAddress)){
            //定义返回map对象
            Map map = new HashMap();
            map.put("code", ResultStatus.ILLEGAL_IP_ERROR.getReturnCode());
            map.put("msg", ResultStatus.ILLEGAL_IP_ERROR.getReturnMsg());
            byte[] bytesMap = null;
            try {
                //把map转换为字节数组
                bytesMap = JSON.toJSONString(map).getBytes("GBK");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            //spring 提供数据缓冲类  使用response.bufferFactory()中提供的wrap方法,把字节数组转换为DataBuffer
            DataBuffer dataBuffer = response.bufferFactory().wrap(bytesMap);
            //writeWith 向响应对象中写入影响数据  需要的是Publisher接口的实现类   Mono就是该接口的实现类
            return response.writeWith(Mono.just(dataBuffer));
        }
        //如果不是,让程序继续运行
        return chain.filter(exchange);
    }
    /**
     * 设置当前过滤器的优先级   返回数字越小,优先级越高   存在多个filter时使用
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}
单点登录过滤器:
```java
package com.aaa.gws.filter;
import com.aaa.gws.service.RemoteUserLogin;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
 * @ fileName:IllegalIpFilter
 * @ description:
 * @ author:zhz
 * @ createTime:2022/2/22 11:32
 * @ version:1.0.0
 */
@Component
public class SSOLoginFilter implements GlobalFilter, Ordered {
    @Resource
    private RemoteUserLogin remoteUserLogin;
    @Value("${white_list}")
    private  String whileList;
   /* @Value("${illegal_ip}")
    private String  illegalIp;*/
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("单点登录过滤。。。。。。。。。。");
        //gateway封装的request请求对象
        ServerHttpRequest request = exchange.getRequest();
        //gateway封装的response响应对象
        ServerHttpResponse response = exchange.getResponse();
        RequestPath path = request.getPath();
        ///order/selectOne    /  jq/chaxunsuoyou
        System.out.println(path+"..............................");
        String[] whiteListArray = whileList.split(",");
       // System.out.println(whiteListArray+".......................");
        for (String permsPath : whiteListArray) {
          //  System.out.println(permsPath+"................ssss.............."+path.toString());
            if(path.toString().contains(permsPath)){
               // System.out.println(permsPath+"................ssss..............");
                //放行
              return  chain.filter(exchange);
            }
        }
        //获取请求所有参数集合 ?a=1&b=2&accessToken=admin1795936b-2ddc-4b24-b286-10ed018c61d88
        MultiValueMap<String, String> queryParams = request.getQueryParams();
        //定义accessTokenValue
        // boolean isHasToken =false;
        String accessTokenValue = "";
        if (queryParams != null) {
            Set<Map.Entry<String, List<String>>> entries = queryParams.entrySet();
            for (Map.Entry<String, List<String>> entry : entries) {
                System.out.println("参数的key:" + entry.getKey() + ",value:" + entry.getValue());
                if (entry.getKey().equals("accessToken")) {
                    // isHasToken =true;
                    accessTokenValue = entry.getValue().get(0);
                }
            }
        }
        //获取IP地址
        InetSocketAddress remoteAddress = request.getRemoteAddress();
        InetAddress address = remoteAddress.getAddress();
        String hostAddress = address.getHostAddress(); //获取客户端Ip
        //System.out.println("请求的IP为:" + hostAddress);
        //System.out.println("accessTokenValue========="+accessTokenValue);
        //如果没有带token获取校验错误,都需要返回错误信息
        if (accessTokenValue.equals("") || !remoteUserLogin.checkToken(accessTokenValue)) {
                    // 不合法则返回错误信息
                    Map<String, Object> map = new HashMap<>();
                    map.put("errorCode", 5003);
                    map.put("errorMessage", "认证错误");
                    try {
                        byte[] datas = JSON.toJSONString(map).getBytes("utf-8");
                        DataBuffer buffer = response.bufferFactory().wrap(datas);
                        //添加响应头
                        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
                        //写入自定义错误信息,并返回
                        return response.writeWith(Mono.just(buffer));
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
             }
            //程序继续运行
            return chain.filter(exchange);
        }
    /**
     * 排序方法  返回值越小  优先级越高
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

3.3.6 gateway跨域配置

spring:
  cloud:
    gateway:
      globalcors: # 全局的跨域处理
        add-to-simple-url-handler-mapping: true  # 解决options请求被拦截问题
        corsConfigurations:
          '[/**]':
            allowedOrigins: # 允许哪些网站的跨域请求
              - "*"  #所有网站
              #- "http://localhost:8090" #指定网站
              #- "http://www.leyou.com"
            allowedMethods: # 允许的跨域ajax的请求方式
              - "*"
              #- "GET"
              #- "POST"
              #- "DELETE"
              #- "PUT"
              #- "OPTIONS"
            allowedHeaders: "*" # 允许在请求中携带的头信息
            allowCredentials: true # 是否允许携带cookie
            maxAge: 360000 # 这次跨域检测的有效期

4,知识点总结

5,本章面试题


相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
目录
相关文章
|
4天前
|
负载均衡 Java 应用服务中间件
Gateway服务网关
Gateway服务网关
13 1
Gateway服务网关
|
2月前
|
Java 开发者 Spring
Spring Cloud Gateway 中,过滤器的分类有哪些?
Spring Cloud Gateway 中,过滤器的分类有哪些?
43 3
|
2月前
|
负载均衡 Java 网络架构
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
91 5
|
1月前
|
负载均衡 Java API
【Spring Cloud生态】Spring Cloud Gateway基本配置
【Spring Cloud生态】Spring Cloud Gateway基本配置
37 0
|
2月前
|
测试技术 微服务
微服务(八)-服务网关zuul(四)
微服务(八)-服务网关zuul(四)
|
2月前
|
监控 前端开发 Java
微服务(七)-服务网关zuul(三)
微服务(七)-服务网关zuul(三)
|
2月前
|
负载均衡 前端开发 安全
微服务(六)-服务网关zuul(二)
微服务(六)-服务网关zuul(二)
|
2月前
|
SpringCloudAlibaba API 开发者
新版-SpringCloud+SpringCloud Alibaba
新版-SpringCloud+SpringCloud Alibaba
|
3月前
|
资源调度 Java 调度
Spring Cloud Alibaba 集成分布式定时任务调度功能
定时任务在企业应用中至关重要,常用于异步数据处理、自动化运维等场景。在单体应用中,利用Java的`java.util.Timer`或Spring的`@Scheduled`即可轻松实现。然而,进入微服务架构后,任务可能因多节点并发执行而重复。Spring Cloud Alibaba为此发布了Scheduling模块,提供轻量级、高可用的分布式定时任务解决方案,支持防重复执行、分片运行等功能,并可通过`spring-cloud-starter-alibaba-schedulerx`快速集成。用户可选择基于阿里云SchedulerX托管服务或采用本地开源方案(如ShedLock)
114 1
|
28天前
|
JSON SpringCloudAlibaba Java
Springcloud Alibaba + jdk17+nacos 项目实践
本文基于 `Springcloud Alibaba + JDK17 + Nacos2.x` 介绍了一个微服务项目的搭建过程,包括项目依赖、配置文件、开发实践中的新特性(如文本块、NPE增强、模式匹配)以及常见的问题和解决方案。通过本文,读者可以了解如何高效地搭建和开发微服务项目,并解决一些常见的开发难题。项目代码已上传至 Gitee,欢迎交流学习。
107 1
Springcloud Alibaba + jdk17+nacos 项目实践
下一篇
无影云桌面