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表达式即可
    愿你眼中有光芒,活成你想要的模样
  • 相关阅读:
    POJ 1811 Prime Test 素性测试 分解素因子
    sysbench的安装与使用
    电脑中已有VS2005和VS2010安装.NET3.5失败的解决方案
    I.MX6 show battery states in commandLine
    RPi 2B Raspbian system install
    I.MX6 bq27441 driver porting
    I.MX6 隐藏电池图标
    I.MX6 Power off register hacking
    I.MX6 Goodix GT9xx touchscreen driver porting
    busybox filesystem httpd php-5.5.31 sqlite3 webserver
  • 原文地址:https://www.cnblogs.com/SmallStrange/p/14578742.html
Copyright © 2011-2022 走看看