背景討論 feign請求 在微服務(wù)環(huán)境中,完成一個http請求,經(jīng)常需要調(diào)用其他好幾個服務(wù)才可以完成其功能,這種情況非常普遍,無法避免。那么就需要服務(wù)之間的通過feignClient發(fā)起請求,獲取需要的 資源。 認證和鑒權(quán) 一般而言,微服務(wù)項目部署環(huán)境中,各個微服務(wù)都是運行在內(nèi)網(wǎng)環(huán)境,網(wǎng)關(guān)服務(wù)負責(zé)請
在微服務(wù)環(huán)境中,完成一個http請求,經(jīng)常需要調(diào)用其他好幾個服務(wù)才可以完成其功能,這種情況非常普遍,無法避免。那么就需要服務(wù)之間的通過feignClient發(fā)起請求,獲取需要的 資源 。
一般而言,微服務(wù)項目部署環(huán)境中,各個微服務(wù)都是運行在內(nèi)網(wǎng)環(huán)境,網(wǎng)關(guān)服務(wù)負責(zé)請求的 路由 ,對外通過nginx暴露給請求者。
這種情況下,似乎網(wǎng)關(guān)這里做一個 認證 ,就可以確保請求者是合法的,至于微服務(wù)調(diào)用微服務(wù),反正都是自己人,而且是內(nèi)網(wǎng),無所謂是否驗證身份了。
我有一個朋友 ,他們公司的項目確實就是這樣做的,正經(jīng)的商業(yè)項目。
講道理,只要框架提供了這樣的功能,那么就有存在的意義,但是,如果涉及權(quán)限的校驗,微服務(wù)之間的feign調(diào)用就需要知道 身份 了,即需要做 鑒權(quán) 。
無論是JWT、還是OAUTH2、還是shiro,大家比較公認的認證、鑒權(quán)方案,就是在請求頭中放一堆東西,然后服務(wù)提供者通過解析這些東西完成認證和鑒權(quán),這些東西俗稱 token 。
在feign調(diào)用中需要解決的就是token傳遞的問題,只有請求發(fā)起者將正確的token傳遞給服務(wù)提供者,服務(wù)提供者才能完成認證&鑒權(quán),進而返回需要的 資源 。
在feign調(diào)用中可能會遇到如下問題:
編寫一個攔截器,在feign請求前,將http請求攜帶的token傳遞給restTemplate。
具體實現(xiàn)方式為:
創(chuàng)建一個Component實現(xiàn)com.nghsmart.ar.context.RequestAttributeContext中的RequestInterceptor接口
重寫apply方法
通過RequestContextHolder對象獲取到RequestAttributes
通過RequestAttributes對象獲取到HttpServletRequest
通過HttpServletRequest對象獲取到請求頭
在請求頭中把token拿出來
將token塞進restTemplate創(chuàng)建的http請求頭中
示例代碼:
BizFeignRequestInterceptor
import com.nghsmart.ar.context.RequestAttributeContext;
import com.nghsmart.common.core.utils.ServletUtils;
import com.nghsmart.common.core.utils.StringUtils;
import com.nghsmart.common.core.utils.ip.IpUtils;
import com.nghsmart.common.security.constant.FeignRequestHeader;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.AbstractRequestAttributes;
import org.springframework.web.context.request.FacesRequestAttributes;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
@Slf4j
@Order(1)
@Component
public class BizFeignRequestInterceptor implements RequestInterceptor {
? ? @Override
? ? public void apply(RequestTemplate requestTemplate) {
? ? ? ? RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
? ? ? ? if (null! = attributes) {
? ? ? ? ? ? ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) attributes;
? ? ? ? ? ? String token = servletRequestAttributes.getRequest().getHeader("token");
? ? ? ? ? ? requestTemplate.header("token",token);
? ? ? ? }
? ? }
}
上述添加BizFeignRequestInterceptor只能解決同步調(diào)用環(huán)境下的token傳遞問題,當(dāng)是異步線程環(huán)境下就GG了。
通過在主線程中主動將RequestAttribute傳遞到子線程中可以解決一部分異步線程中token傳遞的問題,示例代碼如下:
RequestContextHolder.setRequestAttributes(RequestContextHolder.getRequestAttributes(), true);
但是這種方式有弊端,當(dāng)主線程先于子線程結(jié)束的時候,子線程將獲取不到RequestAttribute,原因是Tomcat會在http請求結(jié)束的時候清空數(shù)據(jù)。
我們可以創(chuàng)建一個InheritableThreadLocal用來保存RequestAttribute,這樣就可以完美解決問題了。
實現(xiàn)思路為:
創(chuàng)建一個 RequestAttributeContext,其中維護一個InheritableThreadLocal對象,用來存RequestAttributes
創(chuàng)建一個RequestAttributeInterceptor,實現(xiàn)HandlerInterceptor, WebMvcConfigurer接口,用來在請求開始前把 RequestAttributes 存放到 RequestAttributeContext 中
修改 BizFeignRequestInterceptor ,當(dāng)無法獲取到 RequestAttributes? 的時候,就從 RequestAttributeContext 中獲取
透傳邏輯不變
相關(guān)示例代碼如下:
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestAttributes;
@Slf4j
public class RequestAttributeContext {
private static final ThreadLocal context = new InheritableThreadLocal<>();
public static void setAttribute(RequestAttributes attributes) {
if (null == attributes) {
log.debug("RequestAttributes is null");
}
context.set(attributes);
}
public static RequestAttributes getAttribute() {
return context.get();
}
public static void removeAttribute() {
context.remove();
}
}
import com.alibaba.fastjson.JSON;
import com.nghsmart.ar.context.RequestAttributeContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
@Configuration
public class RequestAttributeInterceptor implements HandlerInterceptor, WebMvcConfigurer {
? ? /**
? ? ?* 重寫 WebMvcConfigurer 的 addInterceptors,將 RequestAttributeInterceptor 添加到攔截器列表
? ? ?*
? ? ?* @param registry
? ? ?*/
? ? @Override
? ? public void addInterceptors(InterceptorRegistry registry) {
? ? ? ? registry.addInterceptor(this).addPathPatterns("/**").excludePathPatterns("/swagger-resources/**", "/v2/api-docs/**");
? ? }
? ? /**
? ? ?* 重寫 HandlerInterceptor 的 preHandle,在請求開始處理前,將 RequestAttribute 存入 RequestAttributeContext
? ? ?*
? ? ?* @param request ?current HTTP request
? ? ?* @param response current HTTP response
? ? ?* @param handler ?chosen handler to execute, for type and/or instance evaluation
? ? ?* @return
? ? ?* @throws Exception
? ? ?*/
? ? @Override
? ? public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
? ? ? ? RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
? ? ? ? RequestAttributeContext.setAttribute(requestAttributes);
? ? ? ? return true;
? ? }
?
}
import com.nghsmart.ar.context.RequestAttributeContext;
import com.nghsmart.common.core.utils.ServletUtils;
import com.nghsmart.common.core.utils.StringUtils;
import com.nghsmart.common.core.utils.ip.IpUtils;
import com.nghsmart.common.security.constant.FeignRequestHeader;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.AbstractRequestAttributes;
import org.springframework.web.context.request.FacesRequestAttributes;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
@Slf4j
@Order(1)
@Component
public class BizFeignRequestInterceptor implements RequestInterceptor {
? ? @Override
? ? public void apply(RequestTemplate requestTemplate) {
? ? ? ? RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
? ? ? ? if (null! = attributes) {
? ? ? ? ? ? ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) attributes;
? ? ? ? ? ? String token = servletRequestAttributes.getRequest().getHeader("token");
? ? ? ? ? ? requestTemplate.header("token",token);
? ? ? ? }else {
? ? ? ? ? ? RequestAttributes requestAttributes = RequestAttributeContext.getAttribute();
? ? ? ? ? ? if (null != requestAttributes) {
? ? ? ? ? ? ? ? RequestContextHolder.setRequestAttributes(requestAttributes);
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? log.debug("requestAttributes is null");
? ? ? ? ? ? }
? ? ? ? ? ? ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
? ? ? ? ? ? String token = servletRequestAttributes.getRequest().getHeader("token");
? ? ? ? ? ? requestTemplate.header("token",token);
? ? ? ? }
? ? }
}
機器學(xué)習(xí):神經(jīng)網(wǎng)絡(luò)構(gòu)建(下)
閱讀華為Mate品牌盛典:HarmonyOS NEXT加持下游戲性能得到充分釋放
閱讀實現(xiàn)對象集合與DataTable的相互轉(zhuǎn)換
閱讀算法與數(shù)據(jù)結(jié)構(gòu) 1 - 模擬
閱讀5. Spring Cloud OpenFeign 聲明式 WebService 客戶端的超詳細使用
閱讀Java代理模式:靜態(tài)代理和動態(tài)代理的對比分析
閱讀Win11筆記本“自動管理應(yīng)用的顏色”顯示規(guī)則
閱讀本站所有軟件,都由網(wǎng)友上傳,如有侵犯你的版權(quán),請發(fā)郵件[email protected]
湘ICP備2022002427號-10 湘公網(wǎng)安備:43070202000427號© 2013~2025 haote.com 好特網(wǎng)