本文转载请联系JAVA日知录公众号。前言在使用了SpringCloud架构之后,我们希望所有的请求都需要经过网关来访问。无需任何处理,我们就可以绕过网关,直接访问后端服务。如下,我们也可以绕过网关,直接访问后端服务来获取数据。那么我们今天的话题就是如何防止请求绕过网关直接访问后端服务呢?解决方案我觉得主要有3种方案可以防止旁路网关直接请求后端服务:使用Kubernetes部署在使用Kubernetes部署SpringCloud架构时,我们给网关的服务配置NodePort,其他后端服务的Service使用ClusterIp,这样集群外只能访问网关。网络隔离的公共后端服务部署在内网,通过防火墙策略只允许网关应用访问后端服务。应用层拦截到后端服务的请求时,通过拦截器检查请求是否来自网关,如果不是来自网关,则提示不允许访问。这里着重在应用层拦截这个解决方案。实现思路实现思路其实很简单。当请求经过网关时,在请求头中增加一个header,在后台服务中编写拦截器,判断请求头是否与网关上设置的请求头一致。如果不是则不允许访问并给出提示。当然,为了避免每个后端服务都需要写这个拦截器,我们可以把它写在一个publicstarter中,让后端服务引用。并且为了灵活性,您可以通过配置决定是否只允许后端服务访问。接下来我们看核心代码。(代码涉及到SpringBoot写publicstarter的套路,相信看过我博客的同学肯定知道,因为之前的文章已经详细提到了。)实现过程在gatewaycloud-gateway模块中写gatewayfilter@Component@Order(0)publicclassGatewayRequestFilterimplementsGlobalFilter{@OverridepublicMonofilter(ServerWebExchangeexchange,GatewayFilterChainchain){byte[]token=Base64Utils.encode((CloudConstant.GATEWAY_TOKEN_VALytes)());String[]headerValues={newString(token)};ServerHttpRequestbuild=exchange.getRequest().mutate().header(CloudConstant.GATEWAY_TOKEN_HEADER,headerValues).build();ServerWebExchangenewExchange=exchange.mutate().request(构建).build();returnchain.filter(newExchange);}}在请求经过网关的时候添加一个额外的Header,为了方便直接设置为固定值。创建一个publicStarter模块cloud-component-security-starter写一个配置类,灵活控制服务是否允许绕过网关@Data@ConfigurationProperties(prefix="javadaily.cloud")publicclassCloudSecurityProperties{/***是否可以onlybeobtainedthroughthegateway资源*默认为True*/privateBooleanonlyFetchByGateway=Boolean.TRUE;}编写拦截器,用于校验请求是否经过网关publicclassServerProtectInterceptorimplementsHandlerInterceptor{privateCloudSecurityPropertiesproperties;@OverridepublicbooleanpreHandle(@NonNullHttpServletRequestrequest,@NonNullHttpServletResponseresponse,@NonNullObjecthandler){if(!properties.getOnlyFetchByGateway()){returntrue;}Stringtoken=request.getHeader(CloudConstant.GATEWAY_TOKEN_HEADER);StringgatewayToken=newString(Base64Utils.encode(CloudConstant.GATEWAY_TOKEN_VALUE.getBytes()));if(StringUtils.equals(gatewayToken,token))){returntrue;}else{ResultDataresultData=newResultData<>();resultData.setSuccess(false);resultData.setStatus(HttpServletResponse.SC_FORBIDDEN);resultData.setMessage("请通过网关访问资源");WebUtils.writeJson(response,resultData);returnfalse;}}publicvoidsetProperties(CloudSecurityPropertiesproperties){this.properties=properties;}}配置拦截器publicclassCloudSecurityInterceptorConfigureimplementsWebMvcConfigurer{privateCloudSecurityPropertiesproperties;@AutowiredpublicvoidsetProperties(CloudSecurityPropertiesproperties){this.properties=properties;}@BeanpublicHandlerInterceptorserverProtectInterceptor(){ServerProtectInterceptorinterceptor=newServerProtectInterceptor();interceptor.setProperties(properties);returninterceptor;}@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){registry.addInterceptor(serverProtectInterceptor());}}编写starter装载类@EnableConfigurationProperties(CloudSecurityProperties.class)publicclassCloudSecurityAutoConfigure{@BeanpublicCloudSecurityInterceptorConfigurecloudSecurityInterceptorConfigure(){returnnewCloudSecurityInterceptorConfigure();}}构建资源文件spring.factories,配置Bean的自动加载org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.javadaily.component.security.configure.CloudSecurityAutoConfigure在后端服务配置文件中添加属性配置。默认情况下,javadaily:cloud:onlyFetchByGateway:true只能通过网关访问。经过以上步骤,一个publicStartermodule只需构建后端服务并引用这个publicStartermodule,以account-service为例com.jianzh5.cloudcloud-component-security-starter实现效果,直接访问后端服务接口http://localhost:8010/account/getByCode/jianzh5返回结果:"message":"请通过网关访问资源“,”状态“:403,”成功“:假,”时间戳“:1611660015830}