All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.taotao.cloud.openfeign.feign.FeignRequestInterceptor Maven / Gradle / Ivy

There is a newer version: 2025.01
Show newest version
/*
 * Copyright (c) 2020-2030, Shuigedeng ([email protected] & https://blog.taotaocloud.top/).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.taotao.cloud.openfeign.feign;

import static com.taotao.cloud.common.constant.CommonConstant.BLANK;
import static com.taotao.cloud.common.utils.lang.StringUtils.NEW_LINE;

import com.taotao.cloud.common.constant.CommonConstant;
import com.taotao.cloud.common.constant.ContextConstant;
import com.taotao.cloud.common.holder.TenantContextHolder;
import com.taotao.cloud.common.holder.TraceContextHolder;
import com.taotao.cloud.common.holder.VersionContextHolder;
import com.taotao.cloud.common.utils.common.IdGeneratorUtils;
import com.taotao.cloud.common.utils.servlet.TraceUtils;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import jakarta.annotation.PostConstruct;
import jakarta.servlet.http.HttpServletRequest;
import java.util.*;
import org.apache.commons.lang3.StringUtils;
import org.dromara.hutool.core.collection.CollUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.springframework.http.HttpHeaders;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * 假装请求拦截器
 *
 * @author shuigedeng
 * @version 2023.07
 * @since 2023-07-06 09:26:57
 */
public class FeignRequestInterceptor implements RequestInterceptor {
    protected List requestHeaders = new ArrayList<>();

    public static final List HEADER_NAME_LIST = Arrays.asList(
            ContextConstant.JWT_KEY_TENANT,
            ContextConstant.JWT_KEY_SUB_TENANT,
            ContextConstant.JWT_KEY_USER_ID,
            ContextConstant.JWT_KEY_ACCOUNT,
            ContextConstant.JWT_KEY_NAME,
            ContextConstant.GRAY_VERSION,
            ContextConstant.TRACE_ID_HEADER,
            "X-Real-IP",
            "x-forwarded-for");

    @PostConstruct
    public void initialize() {
        requestHeaders.add(CommonConstant.TTC_USER_ID_HEADER);
        requestHeaders.add(CommonConstant.TTC_USER_NAME_HEADER);
        requestHeaders.add(CommonConstant.TTC_USER_ROLE_HEADER);
        requestHeaders.add(CommonConstant.TTC_VERSION_HEADER);
    }

    @Override
    public void apply(RequestTemplate template) {
        Map> headers = template.headers();
        Set feignHeaderNames = headers.keySet();

        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // job 类型的任务,可能没有Request
        if (requestAttributes != null) {
            ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
            // RequestContextHolder.setRequestAttributes(attributes, true);
            HttpServletRequest request = attributes.getRequest();
            Enumeration headerNames = request.getHeaderNames();
            if (headerNames != null) {
                while (headerNames.hasMoreElements()) {
                    String headerName = headerNames.nextElement();
                    if (feignHeaderNames.contains(headerName)) {
                        continue;
                    }

                    String values = request.getHeader(headerName);

                    // 跳过content-length值的复制。因为服务之间调用需要携带一些用户信息之类的
                    // 所以实现了Feign的RequestInterceptor拦截器复制请求头,复制的时候是所有头都复制的,可能导致Content-length长度跟body不一致
                    // @see https://blog.csdn.net/qq_39986681/article/details/107138740
                    if (StringUtils.equalsIgnoreCase(headerName, HttpHeaders.CONTENT_LENGTH)) {
                        continue;
                    }

                    // 解决 UserAgent 信息被修改后,AppleWebKit/537.36 (KHTML,like Gecko)部分存在非法字符的问题
                    if (StringUtils.equalsIgnoreCase(headerName, HttpHeaders.USER_AGENT)) {
                        values = StringUtils.replace(values, NEW_LINE, String.valueOf(BLANK));
                    }

                    template.header(headerName, values);
                }
            }

            String token = extractHeaderToken(request);
            if (StrUtil.isEmpty(token)) {
                token = request.getParameter(CommonConstant.ACCESS_TOKEN);
            }
            if (StrUtil.isNotEmpty(token)) {
                addHeader(template, CommonConstant.TTC_TOKEN_HEADER, CommonConstant.BEARER_TYPE + " " + token);
            }
        }

        // 传递client 传递access_token,无网络隔离时需要传递
        String tenant = TenantContextHolder.getTenant();
        if (StrUtil.isBlank(tenant)) {
            tenant = TraceUtils.getTenantId();
        }
        addHeader(template, CommonConstant.TTC_TENANT_ID, StrUtil.isNotBlank(tenant) ? tenant : "1");

        // 传递日志traceId
        String traceId = TraceContextHolder.getTraceId();
        if (StrUtil.isBlank(traceId)) {
            traceId = TraceUtils.getTraceId();
        }
        addHeader(
                template,
                CommonConstant.TTC_TRACE_ID,
                StrUtil.isNotBlank(traceId) ? traceId : IdGeneratorUtils.getIdStr());

        // 传递version
        String version = VersionContextHolder.getVersion();
        if (StrUtil.isBlank(version)) {
            version = TraceUtils.getVersion();
        }
        addHeader(template, CommonConstant.TTC_REQUEST_VERSION, StrUtil.isNotBlank(version) ? version : "");

        // 服务内部inner
        addHeader(template, CommonConstant.TTC_FROM_INNER, "true");
    }

    /**
     * 解析head中的token
     *
     * @param request request
     */
    private String extractHeaderToken(HttpServletRequest request) {
        Enumeration headers = request.getHeaders(CommonConstant.TTC_TOKEN_HEADER);
        while (headers.hasMoreElements()) {
            String value = headers.nextElement();
            if (value.startsWith(CommonConstant.BEARER_TYPE)) {
                String authHeaderValue =
                        value.substring(CommonConstant.BEARER_TYPE.length()).trim();
                int commaIndex = authHeaderValue.indexOf(',');
                if (commaIndex > 0) {
                    authHeaderValue = authHeaderValue.substring(0, commaIndex);
                }
                return authHeaderValue;
            }
        }
        return null;
    }

    private void addHeader(RequestTemplate template, String headerName, String headerValue) {
        Map> headers = template.headers();
        if (StrUtil.isBlank(headerName) || StrUtil.isBlank(headerValue)) {
            return;
        }
        if (headers.containsKey(headerName)) {
            Collection headerValues = headers.get(headerName);
            if (CollUtil.isEmpty(headerValues)) {
                template.header(headerName, headerValue);
            }

            if (!headerValues.contains(headerValue)) {
                template.header(headerName, headerValue);
            }
        } else {
            template.header(headerName, headerValue);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy