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

二、拦截实现
1、Gateway模块添加全局过滤器拦截处理
01 02 03 04 05 06 07 08 09 10 11 12 13 14 | /** * 全局网关 */ @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、服务模块添加全局拦截器校验请求
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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文件
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 | # 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接口
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 | @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"); } } |
这样就可以实现服务传递网关带过来的请求头了。