zoukankan      html  css  js  c++  java
  • Spring Cloud之Feign 转发请求头(header参数)

    1. 自定义feign隔离策略

    package com.cloud.config;
     
    import com.netflix.hystrix.HystrixThreadPoolKey;
    import com.netflix.hystrix.HystrixThreadPoolProperties;
    import com.netflix.hystrix.strategy.HystrixPlugins;
    import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
    import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariable;
    import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableLifecycle;
    import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier;
    import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
    import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher;
    import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
    import com.netflix.hystrix.strategy.properties.HystrixProperty;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestAttributes;
    import org.springframework.web.context.request.RequestContextHolder;
     
    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.Callable;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
     
    /**
     * 自定义Feign的隔离策略:
     *   在转发Feign的请求头的时候, 如果开启了Hystrix, 
     *      Hystrix的默认隔离策略是Thread(线程隔离策略), 因此转发拦截器内是无法获取到请求的请求头信息的, 
     *      可以修改默认隔离策略为信号量模式:hystrix.command.default.execution.isolation.strategy=SEMAPHORE, 
     *      这样的话转发线程和请求线程实际上是一个线程, 这并不是最好的解决方法, 信号量模式也不是官方最为推荐的隔离策略;
     *   另一个解决方法就是自定义Hystrix的隔离策略:
     *      思路是将现有的并发策略作为新并发策略的成员变量,在新并发策略中, 
     *      返回现有并发策略的线程池、Queue;将策略加到Spring容器即可;
     */
    @Component
    public class FeignHystrixConcurrencyStrategyIntellif extends HystrixConcurrencyStrategy {
        Logger log = LoggerFactory.getLogger(this.getClass());
     
        private HystrixConcurrencyStrategy delegate;
     
        public FeignHystrixConcurrencyStrategyIntellif() {
            try {
                this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();
                if (this.delegate instanceof FeignHystrixConcurrencyStrategyIntellif) {
                    // Welcome to singleton hell...
                    return;
                }
     
                HystrixCommandExecutionHook commandExecutionHook =
                        HystrixPlugins.getInstance().getCommandExecutionHook();
     
                HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance().getEventNotifier();
                HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance().getMetricsPublisher();
                HystrixPropertiesStrategy propertiesStrategy =
                        HystrixPlugins.getInstance().getPropertiesStrategy();
                this.logCurrentStateOfHystrixPlugins(eventNotifier, metricsPublisher, propertiesStrategy);
     
                HystrixPlugins.reset();
                HystrixPlugins instance = HystrixPlugins.getInstance();
                instance.registerConcurrencyStrategy(this);
                instance.registerCommandExecutionHook(commandExecutionHook);
                instance.registerEventNotifier(eventNotifier);
                instance.registerMetricsPublisher(metricsPublisher);
                instance.registerPropertiesStrategy(propertiesStrategy);
            } catch (Exception e) {
                log.error("Failed to register Sleuth Hystrix Concurrency Strategy", e);
            }
        }
     
        private void logCurrentStateOfHystrixPlugins(HystrixEventNotifier eventNotifier,
                                                     HystrixMetricsPublisher metricsPublisher,
                                                     HystrixPropertiesStrategy propertiesStrategy) {
            if (log.isDebugEnabled()) {
                log.debug("Current Hystrix plugins configuration is [" + "concurrencyStrategy ["
                        + this.delegate + "]," + "eventNotifier [" + eventNotifier + "]," + "metricPublisher ["
                        + metricsPublisher + "]," + "propertiesStrategy [" + propertiesStrategy + "]," + "]");
                log.debug("Registering Sleuth Hystrix Concurrency Strategy.");
            }
        }
     
        @Override
        public <T> Callable<T> wrapCallable(Callable<T> callable) {
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            return new WrappedCallable<>(callable, requestAttributes);
        }
     
        @Override
        public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
                                                HystrixProperty<Integer> corePoolSize,
                                                HystrixProperty<Integer> maximumPoolSize,
                                                HystrixProperty<Integer> keepAliveTime,
                                                TimeUnit unit, BlockingQueue<Runnable> workQueue) {
            return this.delegate.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize, keepAliveTime,
                    unit, workQueue);
        }
     
        @Override
        public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
                                                HystrixThreadPoolProperties threadPoolProperties) {
            return this.delegate.getThreadPool(threadPoolKey, threadPoolProperties);
        }
     
        @Override
        public BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) {
            return this.delegate.getBlockingQueue(maxQueueSize);
        }
     
        @Override
        public <T> HystrixRequestVariable<T> getRequestVariable(HystrixRequestVariableLifecycle<T> rv) {
            return this.delegate.getRequestVariable(rv);
        }
     
        static class WrappedCallable<T> implements Callable<T> {
            private final Callable<T> target;
            private final RequestAttributes requestAttributes;
     
            public WrappedCallable(Callable<T> target, RequestAttributes requestAttributes) {
                this.target = target;
                this.requestAttributes = requestAttributes;
            }
     
            @Override
            public T call() throws Exception {
                try {
                    RequestContextHolder.setRequestAttributes(requestAttributes);
                    return target.call();
                } finally {
                    RequestContextHolder.resetRequestAttributes();
                }
            }
        }
    }

    2.设置feign拦截器,进行相应header的转发

    2.1 示例一

    package com.cloud.interceptor;
     
    import feign.RequestInterceptor;
    import feign.RequestTemplate;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
     
    import javax.servlet.http.HttpServletRequest;
     
    import static com.cloud.config.Constants.CLOUD_SESSION_ID;
     
    /**
     * feign转发header参数*/
    @Configuration
    public class FeignRequestInterceptor implements RequestInterceptor {
        @Autowired
        HttpServletRequest httpServletRequest;
     
        public FeignRequestInterceptor() {
        }
     
        public void apply(RequestTemplate requestTemplate) {
            HttpServletRequest request = ((ServletRequestAttributes) 
                                   RequestContextHolder.getRequestAttributes()).getRequest();
            String sessionId = RequestContextHolder.currentRequestAttributes().getSessionId();
            String sessionId2  = httpServletRequest.getSession().getId();
            String appkey0     = httpServletRequest.getHeader("appKey");
            String appkey      = request.getHeader("appKey");
            // String traceId     = request.getHeader("traceId");
            // String requestIp   = MyRequestUtil.getIpAddress(request);
            // String userAgent   = request.getHeader("User-agent");
            // String contentType = request.getHeader("Content-Type");
            //
            // requestTemplate.header("Content-Type", new String[]{contentType});
            // requestTemplate.header("User-agent", new String[]{userAgent});
            // requestTemplate.header("User-IP", new String[]{requestIp});
            // requestTemplate.header("traceId", new String[]{traceId});
            // requestTemplate.header("appKey", new String[]{appkey});
     
            requestTemplate.header(CLOUD_SESSION_ID, sessionId);
        }
    }

    2.2 示例一

    转发所有header

    package com.cloud.interceptor;
     
    import feign.RequestInterceptor;
    import feign.RequestTemplate;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
     
    import javax.servlet.http.HttpServletRequest;
    import java.util.Enumeration;
     
    /**
     * 转发所有header*/
    // @Configuration // 注释掉, 不启用该拦截器; 如果启用, 直接打开注释即可
    public class FeignRequestInterceptor3 implements RequestInterceptor {
        private final Logger logger = LoggerFactory.getLogger(getClass());
     
        @Override
        public void apply(RequestTemplate template) {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            Enumeration<String> headerNames = request.getHeaderNames();
            if (headerNames != null) {
                while (headerNames.hasMoreElements()) {
                    String name = headerNames.nextElement();
                    String values = request.getHeader(name);
                    template.header(name, values);
     
                }
                logger.info("feign interceptor header:{}",template);
            }
     
            // 转发参数
            Enumeration<String> bodyNames = request.getParameterNames();
            StringBuilder body = new StringBuilder();
            if (bodyNames != null) {
                while (bodyNames.hasMoreElements()) {
                    String name = bodyNames.nextElement();
                    String values = request.getParameter(name);
                    body.append(name).append("=").append(values).append("&");
                }
            }
            if (body.length() != 0) {
                body.deleteCharAt(body.length() - 1);
                template.body(body.toString());
                //logger.info("feign interceptor body:{}",body.toString());
            }
        }
    }

    2.3 示例一

    客户端带Token 情况

     

    1. 如下图客户端携带token访问A服务。
    2. A服务通过FeginClient 调用B服务获取相关依赖数据。
    3. 所以只要带token 访问A 无论后边链路有多长 ABCD 都可以获取当前用户信息
    4. 权限需要有这些整个链路接口的全部权限才能成功

     

     

    核心代码

    fein 拦截器将本服务的token 通过copyToken的形式传递给下游服务

    public class PigFeignClientInterceptor extends OAuth2FeignRequestInterceptor {
    
        @Override
        public void apply(RequestTemplate template) {
            Collection<String> fromHeader = template.headers().get(SecurityConstants.FROM);
            if (CollUtil.isNotEmpty(fromHeader) && fromHeader.contains(SecurityConstants.FROM_IN)) {
                return;
            }
    
            accessTokenContextRelay.copyToken();
            if (oAuth2ClientContext != null
                && oAuth2ClientContext.getAccessToken() != null) {
                super.apply(template);
            }
        }
    }

    无token请求,服务内部发起情况处理。

     

    [warning]  很多情况下,比如定时任务。A服务并没有token 去请求B服务,对这种情况进行了兼容。类似于A对外暴露API,但是又安全限制。

    • FeignClient 需要带一个请求token,FROM_IN 声明是内部调用
    remoteLogService.saveLog(sysLog, SecurityConstants.FROM_IN);
    • 目标接口对内外调用进行限制
      @Inner 注解,这样就避免接口对外暴露的安全问题。只能通过内部调用才能使用,浏览器不能直接访问该接口
        @Inner
        @PostMapping
        public R save(@Valid @RequestBody SysLog sysLog) {
            return new R<>(sysLogService.save(sysLog));
        }
    • 若代码无法加 @Inner 也可以参考API 对外暴露章节对外暴露目标接口
    ignore:
      urls:
        - 目标借口的Ant表达式即可
    愿你眼中有光芒,活成你想要的模样
  • 相关阅读:
    Wannafly Camp 2020 Day 2C 纳新一百的石子游戏
    [CF653F] Paper task
    [CCPC2019 哈尔滨] L. LRU Algorithm
    [CCPC2019 哈尔滨] A. Artful Paintings
    [BZOJ4310] 跳蚤
    [BZOJ3277/BZOJ3473] 串
    bugku数字验证绕过正则
    sublime在搜索的时候排除js文件
    bugku逗号过滤注入
    SQL注入之逗号拦截绕过
  • 原文地址:https://www.cnblogs.com/SmallStrange/p/14578742.html
Copyright © 2011-2022 走看看