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