问题:
FeignClient代码
@FeignClient(name = "xxx",url = "https://xxxx:1234", configuration = UcFeignConfiguration.class)
@Headers("Content-Type: application/json;charset=utf-8")
public interface UcAgent {
/**
* 按条件获取员工并分页
*
* @param authHeader
* @param staffQueryMap
*/
@GetMapping("/staff/v1/staffs")
List<StaffIdPo> getStaffsWithFilterFilterPartially(
@RequestHeader("Authorization") String authHeader,
@SpringQueryMap Map<String,Object> staffQueryMap);
}
UcFeignConfiguration
@Slf4j
public class UcFeignConfiguration {
private static final String PER_FIX = "Load balancer does not contain an instance";
private static final String UNKNOWN_ERROR = "unknown error";
private static final Integer ERROR_CODE = 59;
private static final Integer HTTP_STATUS_OK = 200;
private static final Integer HTTP_NOT_FOUND = 404;
@Bean
public ErrorDecoder errorDecoder() {
return new UcErrorDecoder();
}
public class UcErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String methodKey, Response response) {
ErrorDetail errorDetail = new ErrorDetail();
if (response.status() != HTTP_STATUS_OK) {
log.info("response request:{}, body:{}, http status:{}", response.request(), response.body(), response.status());
try {
char[] buffer = new char[response.body().length()];
IOUtils.read(response.body().asReader(StandardCharsets.UTF_8), buffer);
String responseBody = new String(buffer);
if (StringUtils.startsWith(responseBody, PER_FIX) && StringUtils.length(responseBody) > ERROR_CODE) {
errorDetail.setDescription(StringUtils.substring(responseBody, ERROR_CODE) + " not exists");
throw new BaseException(HttpStatus.UNAVAILABLE, errorDetail);
}
ErrorResult result = JsonUtils.toObject(responseBody, ErrorResult.class);
log.info("uc api exception result error:{},response:{},response status:{}", JsonUtils.toString(result),
responseBody, response.status());
if (Objects.nonNull(result)) {
return dealUcApiException(response, responseBody);
} else if (response.status() == HTTP_NOT_FOUND) {
throw new UcApiException(404, UNKNOWN_ERROR);
} else {
errorDetail.setDescription(UNKNOWN_ERROR);
throw new BaseException(HttpStatus.UNKNOWN, errorDetail);
}
} catch (IOException e) {
log.error("handle error exception:{}", e);
throw new BaseException(HttpStatus.UNKNOWN, errorDetail);
}
}
throw new BaseException(com.qax.pylon.commons.constant.HttpStatus.UNAVAILABLE, errorDetail);
}
}
}
构建header的方法
/**
* 构建请求头header
* todo 2.4
*
* @return
*/
public String buildAuthHeader() {
long nonce = Long.parseLong(RandomUtil.randomNumbers(10));
long timestamp = System.currentTimeMillis();
final String signature = SignUtil.generateSignature(appKey, secret, timestamp, nonce);
return "TEST-HMAC-SHA256 appKey=" + appKey +
",nonce=" + nonce +
",timestamp=" + timestamp +
",version=1.2.0" +
",signature=" + signature;
}
可以看到构建的header字符串带了逗号,然后发起get请求调用接口,错误日志如下:
response request:GET https://xxxx:1234/staff/v1/staffs:filter-filter-partially?filter_filter.asset_id.id=232323&offset=0&limit=10&keyword&filter_filter.asset_id.oid=23232323 HTTP/1.1
Authorization: TEST-HMAC-SHA256 appKey=XXXXX, nonce=6291065565, timestamp=1644836211277, version=1.2.0, signature=fdfdsfdfdfdfdf
Binary data, body:null, http status:401
header错误导致鉴权失败;
debug到feign.template.HeaderTemplate类下的expand方法
public String expand(Map<String, ?> variables) {
String result;
for(result = super.expand(variables); result.endsWith(","); result = result.replaceAll(",$", "")) {
}
result = result.replaceAll(",", ", ");
return result;
}
可以看到此方法会将header中的逗号替换,替换后的内容是逗号+空格,使用被替换的header发起请求调用其他服务,其他服务在接受到请求后,解析header中的数据的时候出错导致鉴权失败;
解决:使用拦截器替换请求头
@Configuration
@Slf4j
public class FeignConfig implements RequestInterceptor {
private static final String HEADER_KEY = "Authorization";
@Override
public void apply(RequestTemplate requestTemplate) {
Map<String, Collection<String>> headersMap = requestTemplate.headers();
Collection<String> headers = headersMap.get(HEADER_KEY);
headers.stream().forEach(header -> {
if (StringUtils.isNotBlank(header)
&& header.startsWith("TEST-HMAC-SHA256")
&& header.contains(", ")) {
requestTemplate.removeHeader(HEADER_KEY);
requestTemplate.header(HEADER_KEY, header.replaceAll(", ", ","));
}
});
}
}