Spring Cloud Gateway避免绕过网关直接请求服务

在微服务体系架构中,网关承担着重要的角色,在网关中可以添加各种过滤器,过滤请求,保证请求参数安全,限流等等。网关作为系统的入口,需要限制通过服务直接访问,必须经过网关进行统一拦截处理。但通过服务名称或IP端口也可以直接请求服务,如果请求绕过了网关,那就等于绕过了重重关卡,直捣黄龙,所以,在分布式架构中,需要有一定的防范,来确保各个服务相互之间安全调用。

一、解决思路

  • 在网关中给所有请求加上一个请求密钥
  • 在服务添加过滤器,验证密钥
Spring Cloud Gateway避免绕过网关直接请求服务插图

二、拦截实现

1、Gateway模块添加全局过滤器拦截处理

/**
 * 全局网关
 */@Component
public class GatewayFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        PathContainer pathContainer = request.getPath().pathWithinApplication();
        // 添加gatewayKey,防止下游接口直接被访问
        ServerHttpRequest.Builder mutate = request.mutate();
        mutate.header("gatewayKey", "key");
        return chain.filter(exchange.mutate().request(mutate.build()).build());
    }
}

如果考虑到安全性的话,这里header的值也可以使用UUID,并保存至redis中,在其他服务中通过redis读取判断身份。

2、服务模块添加全局拦截器校验请求

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;

@Slf4j
public class GlobalInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object obj) throws Exception {
        String secretKey = request.getHeader("gatewayKey");
        if(!StringUtils.isNotBlank(secretKey) || secretKey.equals("key"){
            response.setContentType("application/json; charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.write("error");
            return false;
        }
        return true;
    }
}

此时,绕过网关,直接访问auth服务的接口会返回error,即必须通过网关。

3、服务之间请求传递请求头

在项目中,发现了feign远程调用会失败,因为在fegin中调用不通过gateway,缺少from的请求头,会被拦截器拦截,导致调用失败。feign调用服务需要传递请求头,要注意feign创建的是一个新的请求,要传递网关的请求需要独立配置。

1)配置application.yml文件

# hystrix 配置
hystrix:
  command:
    default:  #default全局有效,service id指定应用有效
      execution:
        timeout:
          #是否开启超时熔断
          enabled: true
        isolation:
          thread:
            timeoutInMilliseconds: 10000 #断路器超时时间,默认1000ms
          # hystrix 隔离模式改为信号量模式,feign 才能获取到父线程中的请求头(主要配置)
          strategy: SEMAPHORE
          # 允许的并发量,默认值为 10
          semaphore:
            maxConcurrentRequests: 100

2)实现RequestInterceptor接口

@Configuration
public class FeignConfiguration implements RequestInterceptor {
 
    @Override
    public void apply(RequestTemplate template) {
        ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 获取request请求头信息,传递给下一层
        Enumeration<String> headerNames = request.getHeaderNames();
        if (headerNames != null) {
            while (headerNames.hasMoreElements()) {
                String name = headerNames.nextElement();
                String values = request.getHeader(name);
                template.header(name, values);
            }
        }
        // 或独立设置参数
        // template.header("gatewayKey","key");
    }
}

这样就可以实现服务传递网关带过来的请求头了。

发表评论