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