Spring Cloud Alibaba微服务实战三十四 - 隐私接口禁止外部访问

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析


阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

在SpringCloud实战系列文章中曾经介绍过在SpringCloud体系下如何防止前端请求绕过网关直接到达后端微服务,今天我们要反其道而行之,介绍在SpringCloud体系中如何防止内部隐私接口被网关调用。

看到这里可能有的同学会有点晕,怎么还有这种业务场景呢,别急,咱们先回顾一下我们的业务场景。

业务场景

客户端通过网关调用OrderService服务获取数据,OrderService通过Feign调用AccountService服务,而当AccountService提供对应的Feign接口后,客户端是可以通过网关直接调用AccountService接口的。

现在假设AccountService提供的接口包含了部分隐私数据,只允许内部调用协助OrderService进行业务逻辑处理,不允许客户端直接获取,此时咱们需要怎么做?

业务实战

我们先通过代码将原始的流程实现出来,即通过网关调用OrderServiceOrderController,然后在OrderController中通过Feign调用AccountServiceAccountController,为了便于阅读,文章中删除了部分无用代码。

模拟实现

  1. 入口 OrderController
    public class OrderController {
        private final OrderService orderService;
        private final AccountClient accountClient;
    
    
        @GetMapping("/order/{orderNo}")
        public ResultData<OrderDTO> getById(@PathVariable("orderNo") String orderNo){
            OrderDTO orderDTO = orderService.selectByNo(orderNo);
            ResultData<String> secretValue = accountClient.getSecretValue();
            log.info(secretValue);
            return ResultData.success(orderDTO);
        }
    }

OrderController中通过AccountClient调用AccountService

    ResultData<String> secretValue = accountClient.getSecretValue();
  1. Feign接口
    public interface AccountApi {
      ...
        @GetMapping("/account/getSecretValue")
        ResultData<String> getSecretValue();
      ...
    }
  1. AccountController实现
    @RestController
    @Log4j2
    @Api(tags = "account接口")
    @RequiredArgsConstructor(onConstructor = @__(@Autowired))
    public class AccountController implements AccountApi {
    
        /**
         * 隐私接口,禁止通过网关访问
         */
        @Override
        @GetMapping("/account/getSecretValue")
        public ResultData<String> getSecretValue() {
            return ResultData.success("隐私接口,禁止通过网关访问");
        }
        
    }

正如我们前面所说,一旦提供了Feign接口,在默认情况下我们可以直接通过网关访问getSecretValue()方法,那怎么确保这个方法不让外部调用呢?

解决方案

网上现在大部分的解决办法是基于黑名单机制,即将这些接口放入“黑名单”中存储起来,在网关启动时读取黑名单配置,然后校验是否在黑名单中。

这种办法确实也可以,但是总感觉不够灵活,而且实现也比较繁琐,这里就不展开了。

我们今天介绍的是利用访问路径来实现,非常简单轻便。

实现原理

我们需要借助接口路径规范来实现,即给接口指定访问路径时采用这样的格式 : /访问控制/接口

访问控制可以有以下几个规则(参考JAVA包规范),可根据业务需要进行扩展。

    pb - public 所有请求均可访问
    
    pt - protected 需要进行token认证通过后方可访问
    
    pv - private 无法通过网关访问,只能微服务内部调用
    
    df - default 网关请求token认证,并且请求参数和返回结果进行加解密
    
    ...

有了这套接口规范以后,我们就可以灵活控制接口访问权限,然后在网关对接口路径进行校验,如果命中对应的访问控制规则就进行对应的逻辑处理。

代码实战

既然知道了实现原理,那写代码就很简单了。

  1. 修改接口访问路径,遵循接口路径规范
    public interface AccountApi {
        @GetMapping("/pv/account/getSecretValue")
        ResultData<String> getSecretValue();
    }

修改feign的访问路径。

    @RestController
    @Log4j2
    @Api(tags = "account接口")
    @RequiredArgsConstructor(onConstructor = @__(@Autowired))
    public class AccountController implements AccountApi {
    
        /**
         * 隐私接口,禁止通过网关访问
         */
        @Override
        @GetMapping("/pv/account/getSecretValue")
        public ResultData<String> getSecretValue() {
            return ResultData.success("隐私接口,禁止通过网关访问");
        }
        
    }

修改接口实现类的访问路径,这里需要与Feign的路径保持一致。

  1. 网关自定义拦截器进行接口校验
    @Component
    @Order(0)
    @Slf4j
    public class GatewayRequestFilter implements GlobalFilter {
    
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            //获取请求路径
            String rawPath = exchange.getRequest().getURI().getRawPath();
    
            if(isPv(rawPath)){
                throw new HttpServerErrorException(HttpStatus.FORBIDDEN,"can't access private API");
            }
            return chain.filter(newExchange);
        }
    
        /**
         * 判断是否内部私有方法
         * @param requestURI 请求路径
         * @return boolean
         */
        private boolean isPv(String requestURI) {
            return isAccess(requestURI,"/pv");
        }
    
        /**
         * 网关访问控制校验
         */
        private boolean isAccess(String requestURI, String access) {
            //后端标准请求路径为 /访问控制/请求路径
            int index = requestURI.indexOf(access);
            return index >= 0 && StringUtils.countOccurrencesOf(requestURI.substring(0,index),"/") < 1;
        }
    
    }

通过上面简单两步我们就能实现本文提出的问题了,接下来我们测试一下。

测试

  1. 直接访问后端服务,提示无法访问

  1. 通过OrderService访问后端服务正常访问

小结

让内部隐私接口不被外部访问,我相信做微服务开发的同学基本都会遇到。本文中提供的解决方案代码量很少而且接口路径规范可以根据自己的业务规则进行修改扩展,推荐大家使用。其实代码不是关键,关键在于要让团队共同遵守这个接口规范,思想比实现更重要。

  • 24
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值