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