赞
踩
在Spring cloud项目开发中,经常需要通过feign远程调用其他的服务的方法。有时因为跨服务调用的时间较长,就会存在需要异步跨服务调用的场景,此时就会出现请求头(主要是token)丢失的问题。
报错:
Full authentication is required to access this resource
1 异步生成Excel文件
2 跨服务调用上传文件
3 返回文件id
4 回更业务表
思路:从request中获取token;再将token设置到上下文工具中
基于ThreadLocal类封装的工具类,用于在同一线程中的封装数据和获取数据。
/** * 上下文工具类 */ public class BaseContextHandler { private static final ThreadLocal<Map<String, String>> THREAD_LOCAL = new ThreadLocal(); public BaseContextHandler() { } public static void set(String key, Object value) { Map<String, String> map = getLocalMap(); map.put(key, value == null ? "" : value.toString()); } public static <T> T get(String key, Class<T> type) { Map<String, String> map = getLocalMap(); return Convert.convert(type, map.get(key)); } public static <T> T get(String key, Class<T> type, Object def) { Map<String, String> map = getLocalMap(); return Convert.convert(type, map.getOrDefault(key, String.valueOf(def == null ? "" : def))); } public static String get(String key) { Map<String, String> map = getLocalMap(); return (String)map.getOrDefault(key, ""); } public static Map<String, String> getLocalMap() { Map<String, String> map = (Map)THREAD_LOCAL.get(); if (map == null) { map = new HashMap(10); THREAD_LOCAL.set(map); } return (Map)map; } public static void setLocalMap(Map<String, String> threadLocalMap) { THREAD_LOCAL.set(threadLocalMap); } public static String getToken() { return (String)get("Authorization", String.class); } public static void setToken(String token) { set("Authorization", token); } public static void remove() { if (THREAD_LOCAL != null) { THREAD_LOCAL.remove(); } } }
在请求头中获取不到数据时,从BaseContextHandler里面取ThreadLocal存起来的请求头。
/** * feign添加请求头拦截器 */ public class FeignAddHeaderRequestInterceptor implements RequestInterceptor { private static final Logger log = LoggerFactory.getLogger(FeignAddHeaderRequestInterceptor.class); private static final List<String> HEADER_NAME_LIST = Arrays.asList("Authorization", "x-trace-id", "X-Real-IP", "x-forwarded-for"); public FeignAddHeaderRequestInterceptor() { } public void apply(RequestTemplate template) { if (TracingContext.tracing().hasGroup()) { template.header("X-FEIGN-ID", new String[]{TracingContext.tracing().groupId()}); } RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); if (requestAttributes == null) { HEADER_NAME_LIST.forEach((headerName) -> { template.header(headerName, new String[]{BaseContextHandler.get(headerName)}); }); } else { HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest(); if (request == null) { log.warn("path={}, 在FeignClient API接口未配置FeignConfiguration类, 故而无法在远程调用时获取请求头中的参数!", template.path()); } else { HEADER_NAME_LIST.forEach((headerName) -> { template.header(headerName, new String[]{(String)StringUtils.defaultIfEmpty(request.getHeader(headerName), BaseContextHandler.get(headerName))}); }); } } }
@GetMapping("/asyncFeignTest") public R asyncFeignTest() { try { // 第一步:在主线程中, 通过请求头获取token RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes(); HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest(); String token = request.getHeader("Authorization"); // 调用异步方法时,将token传入 asyncFeignTestService.asyncFeignTest(token); return R.success("成功"); } catch (Exception e) { log.error("异常>>>>>>>>>>>>>>>>>>", e); return R.fail("失败"); } }
public interface AsyncFeignTestService{
void asyncFeignTest(String token);
Long feignFileUpload(String token);
}
/** * 调用生成文件的方法,并获取返回的数据 * @return * @throws IOException */ @Async @Override public void asyncFeignTest(String token) { try { // 调用含feign调用的方法,并获取返回的数据 Long resultId = this.feignFileUpload(token); if (resultId != null) { log.info("feign调用服务返回id成功,resultId:{}", resultId); // 回更业务表代码 // ... } } catch (Exception e) { log.error("feign调用服务返回id方法异常:>>>>>>>>>>>>{}", e); } }
/** * 生成文件并保存到文件服务 * @return * @throws IOException */ @Override public Long feignFileUpload(String token) throws IOException { // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 文件生成准备 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // 获取模版文件 ClassPathResource classPathResource = new ClassPathResource("xls_template/file_template_fixed.xlsx"); InputStream fis = classPathResource.getInputStream(); // 保存写入的数据流 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); try { // 设置excel输出策略 ExcelWriter excelWriter = EasyExcel .write(outputStream, InternalSolutionFillDTO.class) .withTemplate(fis) .build(); // 转为文件上传 String fileName = "文件"+System.currentTimeMillis()+".xlsx"; String originName = "file_"+System.currentTimeMillis()+".xlsx"; MockMultipartFile customerFile = new MockMultipartFile(fileName,originName , "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" , outputStream.toByteArray()); // 第二部:在调用feign方法前,设置token BaseContextHandler.setToken(token); // 上传文件并获取主键 R uploadFileVo = fileApi.upload(customerFile, "bucket"); if (uploadFileVo.getData() != null) { FileResultVO customerFileVo = JSONUtil.toBean(JSONUtil.toJsonStr(uploadFileVo.getData()), FileResultVO.class); if (customerFileVo != null && customerFileVo.getId() != null) { return customerFileVo.getId(); } } } catch (Exception e) { log.error("文件生成,方法异常>>>>>>>>>>>>>>", e); } finally { fis.close(); } return null; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。