Java SpringCloud GateWay

1、网关应用场景

  • 认证授权
  • 统一外部入口
  • 请求路由
  • 请求限流
  • 请求日志和监控

    2、Spring Cloud Zuul

  • 服务路由

  • 自定义过滤器:通过实现ZuulFilter类的如下方法
    • filterType() :过滤类型
    • filterOrder():过滤顺序
    • shouldFilter():是否需要过滤
    • run():过滤逻辑

      A.Zuul相关依赖

      1. <dependency>
      2. <groupId>org.springframework.cloud</groupId>
      3. <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
      4. </dependency>

      B.在配置文件配置路由拦截-application.yml

      路由策略配置

      zuul.routes对应的是多个服务的Map,然后每个服务对应的服务ID和Path等等
      直接使用服务名,需要将微服务名称暴露给用户,会存在安全性问题。所以,可以自定义路径来替代微服务名称,即自定义路由策略。
      1. zuul:
      2. routes:
      3. fcant:
      4. path: /fcant-filter

      统一前缀的配置方式

      1. zuul:
      2. prefix: /zuul
      这样就需要通过 localhost:9000/zuul/consumer1/studentInfo/update 来进行访问了。

      服务名屏蔽

      1. zuul:
      2. ignore-services: "*"

      路径屏蔽

      Zuul 还可以指定屏蔽掉的路径 URI,即只要用户请求中包含指定的 URI 路径,那么该请求将无法访问到指定的服务。通过该方式可以限制用户的权限。
      1. zuul:
      2. ignore-patterns: **/auto/**

      * 代表匹配多级任意路径 代表匹配一级任意路径

敏感请求头屏蔽

默认情况下,像 Cookie、Set-Cookie 等敏感请求头信息会被 zuul 屏蔽掉,可以将这些默认屏蔽去掉,当然,也可以添加要屏蔽的请求头。

C.在启动类添加启用Zuul代理的注解

  1. package com.fcant.springcloudgateway;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
  5. @EnableZuulProxy
  6. @SpringBootApplication
  7. public class SpringCloudGateWayApplication {
  8. public static void main(String[] args) {
  9. SpringApplication.run(SpringcloudgatewayApplication.class, args);
  10. }
  11. }

D.Zuul 的过滤功能-认证授权路由范例-AuthorizeFilter.java

实现自定义的 Filter 只需要继承 ZuulFilter 然后将这个过滤器类以 @Component 注解加入 Spring 容器中就行了。
过滤器类型:Pre、Routing、Post。前置Pre就是在请求之前进行过滤,Routing路由过滤器就是路由策略,而Post后置过滤器就是在 Response 之前进行过滤的过滤器。
image.png

  1. package com.fcant.springcloudgateway.filter;
  2. import com.netflix.zuul.ZuulFilter;
  3. import com.netflix.zuul.context.RequestContext;
  4. import com.netflix.zuul.exception.ZuulException;
  5. import org.slf4j.Logger;
  6. import org.slf4j.LoggerFactory;
  7. import org.springframework.http.HttpStatus;
  8. import org.springframework.stereotype.Component;
  9. import javax.servlet.http.HttpServletRequest;
  10. import java.util.Objects;
  11. import java.util.UUID;
  12. /**
  13. * AuthorizeFilter
  14. * <p>
  15. * encoding:UTF-8
  16. *
  17. * @author Fcant 19:23 2019/12/8
  18. */
  19. @Component
  20. public class AuthorizeFilter extends ZuulFilter {
  21. private static final Logger LOGGER = LoggerFactory.getLogger(AuthorizeFilter.class);
  22. private static String accessToken;
  23. public AuthorizeFilter() {
  24. accessToken = UUID.randomUUID().toString();
  25. LOGGER.info("==> accessToken : {}", accessToken);
  26. }
  27. /**
  28. * 外部请求 -> zuul:pre -> 选择路由的服务:routing -> 请求服务:post -> zuul
  29. * pre: 在请求路由之前执行
  30. * routing: 在请求路由之后执行
  31. * post: 在请求到路由对应的服务之后执行
  32. * error: 在其他阶段发生错误时执行
  33. *
  34. * @return filterType
  35. * @author Fcant 20:59 2019/12/8
  36. */
  37. @Override
  38. public String filterType() {
  39. return FilterConstants.PRE_TYPE;
  40. }
  41. /**
  42. * 过滤器执行的顺序:数字越小优先级约高
  43. *
  44. * @return 返回代表过滤器执行顺序等级的数字
  45. * @author Fcant 21:07 2019/12/8
  46. */
  47. @Override
  48. public int filterOrder() {
  49. return 0;
  50. }
  51. /**
  52. * 需不需要执行此过滤器
  53. *
  54. * @return True为执行
  55. * @author Fcant 21:07 2019/12/8
  56. */
  57. @Override
  58. public boolean shouldFilter() {
  59. return true;
  60. }
  61. /**
  62. * 具体的过滤逻辑
  63. *
  64. * @return 返回值被忽略
  65. * @author Fcant 21:08 2019/12/8
  66. */
  67. @Override
  68. public Object run() throws ZuulException {
  69. // 获取请求的上下文,注意导入时导入zuul包下的,而非apache包下的
  70. RequestContext currentContext = RequestContext.getCurrentContext();
  71. HttpServletRequest request = currentContext.getRequest();
  72. String accessToken = request.getParameter("access_token");
  73. // 模拟accessToken验证授权
  74. // 使用Objects.equals(Object1, Object2)比较两个对象的一直性更具安全性
  75. if (Objects.equals(accessToken, AuthorizeFilter.accessToken)) {
  76. currentContext.setResponseStatusCode(HttpStatus.OK.value());
  77. currentContext.setResponseBody("Authorized");
  78. // 此处设置停止路由,因为模拟请求未做其他API转发处理
  79. currentContext.setSendZuulResponse(false);
  80. } else {
  81. // 如果验证不通过就返回springframework.http.HttpStatus提供的HTTP状态码
  82. currentContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
  83. currentContext.setResponseBody(HttpStatus.UNAUTHORIZED.getReasonPhrase());
  84. // 此处设置停止路由转发,因为模拟请求未做其他API转发处理
  85. currentContext.setSendZuulResponse(false);
  86. }
  87. return null;
  88. }
  89. }

E.使用配置的服务拦截路径访问-http://localhost:8080/fcant-filter

此时返回的是未授权
image.png

F.为请求添加参数打印的随机Token访问状态为Authorized

image.png

G.令牌桶限流

首先会定义一个桶,如果桶里面没有满那么就会以一定固定的速率 会往里面放令牌,一个请求过来首先要从桶中获取令牌,如果没有获取到,那么这个请求就拒绝,如果获取到那么就放行。

  1. @Component
  2. @Slf4j
  3. public class RouteFilter extends ZuulFilter {
  4. // 定义一个令牌桶,每秒产生2个令牌,即每秒最多处理2个请求
  5. private static final RateLimiter RATE_LIMITER = RateLimiter.create(2);
  6. @Override
  7. public String filterType() {
  8. return FilterConstants.PRE_TYPE;
  9. }
  10. @Override
  11. public int filterOrder() {
  12. return -5;
  13. }
  14. @Override
  15. public Object run() throws ZuulException {
  16. log.info("放行");
  17. return null;
  18. }
  19. @Override
  20. public boolean shouldFilter() {
  21. RequestContext context = RequestContext.getCurrentContext();
  22. if(!RATE_LIMITER.tryAcquire()) {
  23. log.warn("访问量超载");
  24. // 指定当前请求未通过过滤
  25. context.setSendZuulResponse(false);
  26. // 向客户端返回响应码429,请求数量过多
  27. context.setResponseStatusCode(429);
  28. return false;
  29. }
  30. return true;
  31. }
  32. }

3、网关的跨域配置

A.SpringCloud GateWay网关配置跨域

SpringCloud GateWay通过参考官网跨域配置在yml文件配置即可生效
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#cors-configuration
image.png

  1. spring:
  2. application:
  3. name: gateway
  4. cloud:
  5. gateway:
  6. globalcors:
  7. cors-configurations:
  8. '[/**]':
  9. # 允许携带认证信息
  10. # 允许跨域的源(网站域名/ip),设置*为全部
  11. # 允许跨域请求里的head字段,设置*为全部
  12. # 允许跨域的method, 默认为GET和OPTIONS,设置*为全部
  13. # 跨域允许的有效期
  14. allow-credentials: true
  15. allowed-origins: "*"
  16. allowed-headers: "*"
  17. allowed-methods: "*"
  18. max-age: 3600
  19. add-to-simple-url-handler-mapping: true

:::info 如果需要支持HTTP的预请求需要将 spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping 属性为 true 。 :::

B.SpringCloud Zuul网关配置跨域

因为spring-cloud-starter-netflix-zuul依赖默认引入了spring-boot-starter-web依赖
image.png
所以配置跨域需要通过配置类才能生效

  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.web.cors.CorsConfiguration;
  4. import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
  5. import org.springframework.web.filter.CorsFilter;
  6. /**
  7. * CorsAutoConfiguration
  8. * <p>
  9. * encoding:UTF-8
  10. *
  11. * @author Fcant 上午 10:22 2021/1/5/0005
  12. */
  13. @Configuration
  14. public class CorsAutoConfiguration {
  15. @Bean
  16. public CorsFilter corsFilter(){
  17. //配置初始化对象
  18. CorsConfiguration configuration=new CorsConfiguration();
  19. //允许跨域的域名,如果要携带cookie,不能写* ,*:代表所有的域名都可以访问
  20. configuration.addAllowedOrigin("*");
  21. configuration.setAllowCredentials(true);
  22. configuration.addAllowedMethod("*"); //代表所有的请求方法
  23. configuration.addAllowedHeader("*"); //允许携带任何头信息
  24. //初始化cors配置源对象
  25. UrlBasedCorsConfigurationSource configurationSource=new UrlBasedCorsConfigurationSource();
  26. configurationSource.registerCorsConfiguration("/**",configuration);
  27. //返回corsFilter实例,参数:cors配置源对象
  28. return new CorsFilter(configurationSource);
  29. }
  30. }