【Feign】服务调用安全问题

一、前言

微服务之间需要通信,属于内部通讯,同时会有一些安全问题:

  1. 内部接口不能被外部访问。
  2. 在传统微服务中,内部通讯默认为安全,不需要鉴权。

在工作中,一个外部请求转换内部请求如图:
请添加图片描述

所有的请求通过 api 网关进入,转换为内部请求,会有如下场景:

  1. 外部请求经过 api 网关,进行鉴权:例如用户登录 token 机制
  2. 外部请求经过 api 网关,无需鉴权:例如获取平台公共信息,将对应接口地址放入 ignore-urls 配置中
  3. 内部请求,默认可信,一般不经过网关,由其调用服务进行负载均衡去直接调用其他服务

举个栗子:管理员通过 cms 删除用户

  • 正常流程:请求先打到 cms 服务,管理员鉴权完后,调用 user服务,删除对应的用户B。
  • 异常流程:用户A登录后,通过接口访问,直接去删除用户B。

请添加图片描述

本文就来讨论,如何阻止非法间接内部访问。

先来回顾下服务调用 feign 使用方式:

  1. 服务A 定义 controller
@RestController
@RequestMapping(path = "/test")
public class TestController {

    @GetMapping("/feign")
    public String test() {
    
        return "test feign success";
    }
}
  1. 服务B 定义 feign
@FeignClient(value = "test")
public interface TestClient {
    
    @GetMapping(value = "/test/feign")
    String test();
} 

服务B 通过 feign 调用服务A的接口。



二、设计

为放在内部接口直接对外提供,可从这几个方向考虑:

  1. api 网关转换外部请求时打上标签(或者删除对应 tag
  2. 服务调用每次调用都带上对应标识:例如在 HTTPheader 加入特殊参数
  3. 微服务针对内部接口定义拦截器:只有识别是内部接口才能访问

(1)feign 配置

每个内部请求都带上一个标识:x-inner-request:true

@Component
public class FeignInnerRequestInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        template.header("x-inner-request", true);
    }
}

只需要在将配置放在第三方 jar 指定的文件中即可,使用者会自动加载,从而避免的代码的侵入:

  1. 在资源目录下新建目录 META-INF
  2. META-INF 目录下新建文件 spring.factories
  3. 在文件中添加下面配置:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.test.config.InternalApiInterceptor, \
cn.test.config.FeignInnerRequestInterceptor, \
cn.test.config.InternalApiConfig

(2)网关过滤

每个外部请求转内部请求都带上一个标识:x-inner-request:false

这里使用 gateway

@Configuration
public class InternalApiGatewayFilter {

    @Bean
    @Order(1)
    public GlobalFilter filterInteralApi() {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            request = exchange.getRequest().mutate().header("x-inner-request", false).build();
            return chain.filter(exchange.mutate().request(request).build());
        };
    }
}

(3)微服务内部请求拦截器

这需要两个部分:

  1. 注解:用来表示方法、类是内部访问的。
  2. 拦截器:用来统一处理拦截请求
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface InternalApi {
    /**
     * 是否内部接口, 默认 true
     * @return 是否
     */
    boolean value() default true;
}

拦截器:

@Slf4j
@Component
public class InternalApiInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {

        InternalApi internalApi = getAnnotation(handler);

        if (Objects.isNull(internalApi)) {
 
            return true;
        }

        boolean isInternalApi = Boolean.valueOf(request.getHeader("x-inner-request"));
        
        if (!isInternalApi) {
            
            throw new Exception(); // 抛出异常不给访问
        }

        return true;
    }
    
    private InternalApi getAnnotation(Object handler) {

        HandlerMethod handlerMethod = (HandlerMethod) handler;

        InternalApi internalApi = handlerMethod.getMethodAnnotation(InternalApi.class);

        if (Objects.isNull(internalApi)) {

            internalApi = handlerMethod.getMethod()
                .getDeclaringClass().getAnnotation(InternalApi.class);
        }

        return internalApi;
    }

    private <A extends Annotation> A getAnnotation(HandlerMethod handlerMethod, 
                                                   Class<A> annotationType) {

        A annotation = handlerMethod.getMethodAnnotation(annotationType);

        if (Objects.isNull(annotation)) {

            return handlerMethod.getMethod()
                .getDeclaringClass().getAnnotation(annotationType);
        }

        return annotation;
    }
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值