feign 请求携带请求头解决方案

cloud 进行服务间调用时通常需要添加token作为请求头,下面是我自己的解决方案

@Autowired
    OauthClient oauthClient;
    /**
     * 通过服务名调用
     */
    private static final String OAUTH_URL = "http://sclp-oauth";

    @PostMapping("/login")
    public Result login(@RequestBody Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
        //设置请求头参数
        HttpHeaders headers = new HttpHeaders();
        //spring-cloud-oauth2-server /oauth/token 必须添加一下请求头
        headers.add("Authorization", "Basic c2NscC1jbGllbnQ6c2VjcmV0");
        headers.add("Content-Type", "application/x-www-form-urlencoded");

        //授权类型
        MultiValueMap params = new LinkedMultiValueMap();
        params.add("grant_type", "password");
        //添加请求参数
        for (Map.Entry e : parameters.entrySet()) {
            params.add(e.getKey(), e.getValue());
        }
        //发送请求
        HttpEntity<String> ans = restTemplate.exchange(OAUTH_URL + "/oauth/token",
                HttpMethod.POST,
                new HttpEntity<>(params, headers),
                String.class);

        JSONObject jsonObject = JSONObject.parseObject(ans.getBody().toString());
        String access_token = (String) jsonObject.get("access_token");
        if (Objects.nonNull(access_token) && !Objects.equals("", access_token)) {
            Map<String, String> result = new HashMap<>(1);
            result.put("token", "bearer " + access_token);
            return Result.OK("登录成功!", result);
        } else {
            return Result.ERROR("登录失败!");
        }
    }

但是上面的方案需要对每个请求添加请求头以及参数,非常麻烦,且没有使用到feign这种成熟的微服务调用方案,网上查询了一番,了解到可以统一给feign客户端添加请求头

/**
 * @author JTY
 * @date 21-5-9 21:28
 * @description feign请求添加token、以及其他信息
 */
@Slf4j
public class FeignClientRequestInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        HttpServletRequest httpServletRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        if (httpServletRequest != null) {
            //获取头信息
            Map<String, String> headers = getHeaders(httpServletRequest);
            // 传递所有请求头,防止部分丢失
            Set<Map.Entry<String, String>> headerSet = headers.entrySet();

            //RequestTemplate添加请求头
            for (Map.Entry<String, String> e :
                    headerSet) {
                template.header(e.getKey(), e.getValue());
            }
            // 请求客户端信息、权限信息
            if (httpServletRequest.getHeader(HttpHeaders.AUTHORIZATION) == null) {
                //添加客户端信息
                byte[] auth64Encode = Base64.getEncoder().encode(ConstantString.CLIENT_ID_SECRET.getBytes(StandardCharsets.UTF_8));
                template.header(HttpHeaders.AUTHORIZATION, new String(auth64Encode));
            }
            log.debug("FeignRequestInterceptor:{}", template.toString());
        }
    }

    /**
     * 获取头信息
     *
     * @param request
     * @return
     */
    private Map<String, String> getHeaders(HttpServletRequest request) {
        Map<String, String> headerMap = new LinkedHashMap<>();
        Enumeration<String> enumeration = request.getHeaderNames();
        if (Objects.nonNull(enumeration)) {
            while (enumeration.hasMoreElements()) {
                String key = enumeration.nextElement();
                String value = request.getHeader(key);
                headerMap.put(key, value);
            }
        }
        return headerMap;
    }
}
#开启feign hystrix 服务降级
feign:
  hystrix:
    enabled: false  #true开启histrix 熔断,false 关闭熔断
  client:
    config:
      default:
        connectTimeout: 30000
        readTimeout: 30000
        loggerLevel: full #日志级别
        requestInterceptors: com.sclp.config.FeignClientRequestInterceptor #自定义请求拦截器

此时需要关闭histrix熔断才会有效,否则HttpServletRequest httpServletRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();获取到的request为空(由于histrix隔离策略默认且推荐使用Thread线程隔离,线程间无法共享request内容,因此获取到空,相关内容https://github.com/Netflix/Hystrix/wiki/How-it-Works#Isolation)

#开启feign hystrix 服务降级
feign:
  hystrix:
    enabled: false  #true开启histrix 熔断,false 关闭熔断

若要开启histrix熔断,则可将熔断策略更换为信号量SEMAPHORE,另外可以自定义隔离策略共享线程间数据,
参考自https://zhuanlan.zhihu.com/p/32046755

/**
 * @author JTY
 * @date 21-5-9 22:33
 * @description
 */
@Component
@Slf4j
public class FeignHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {

    private HystrixConcurrencyStrategy delegate;
    public FeignHystrixConcurrencyStrategy() {
        try {
            this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();
            if (this.delegate instanceof FeignHystrixConcurrencyStrategy) {
                // 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.getInstance().registerConcurrencyStrategy(this);
            HystrixPlugins.getInstance()
                    .registerCommandExecutionHook(commandExecutionHook);
            HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);
            HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
            HystrixPlugins.getInstance().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();
            }
        }
    }
}
原文地址:https://www.cnblogs.com/jinit/p/14826282.html