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();
            }
        }
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM