1,课程回顾
2,本章重点
zuul(zuul1,zuul2)
gateway
3,具体内容
3.1 zuul
3.1.1 zuul简介
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) 创建配置
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,本章面试题