在上一片文章中我们解决了网关层认证后向后端服务传递用户信息的问题。今天我们来解决另外一个问题:如何在 OpenFeign 中传递 Token,并且保证多线程情况下也能适用。
如果我们想要在OpenFeign中获取到用户信息,我们可以通过如下步骤实现
实现RequestInterceptor接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class PomeloFeignRequestInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate requestTemplate) { Map<String, String> headerMap = RequestHeaderContextHolder.getInstance().get(); if (ObjectUtil.isNotEmpty(headerMap)) { // heders头透传 for (Map.Entry<String, String> entry : headerMap.entrySet()) { requestTemplate.header(entry.getKey(), entry.getValue()); } } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| public class RequestHeaderContextHolder { // 使用InheritableThreadLocal,使得共享变量可被子线程继承 private final ThreadLocal<Map<String, String>> REQUEST_HEADER_HOLDER; private RequestHeaderContextHolder() { this.REQUEST_HEADER_HOLDER = new InheritableThreadLocal<>() { @Override protected Map<String, String> initialValue() { return new HashMap<>(); } }; } /** * return RequestHeaderHolder instance */ public static RequestHeaderContextHolder getInstance() { return SingletonHolder.instance; } /** * 根据键获取请求头的值 * @param key 请求头的键 * @return 对应键的值 */ public String getValue(String key) { return this.REQUEST_HEADER_HOLDER.get().get(key); } /** * 获取当前线程的请求头信息 * @return 请求头Map */ public Map<String, String> get() { return this.REQUEST_HEADER_HOLDER.get(); } /** * 获取当前线程的用户信息 * @return 当前用户信息 */ public String getCurrentUser() { return this.REQUEST_HEADER_HOLDER.get().get(SecurityConstants.USER_ID_HEADER); }
public String getCurrentUserName() { return this.REQUEST_HEADER_HOLDER.get().get(SecurityConstants.USER_NAME_HEADER); } /** * 设置当前线程的用户信息 * @param userId 用户ID */ public void setCurrentUser(String userId) { this.REQUEST_HEADER_HOLDER.get().put(SecurityConstants.USER_ID_HEADER, userId); }
public void setCurrentUser(String userId, String userName) { this.REQUEST_HEADER_HOLDER.get().put(SecurityConstants.USER_ID_HEADER, userId); this.REQUEST_HEADER_HOLDER.get().put(SecurityConstants.USER_NAME_HEADER, userName); }
/** * 设置请求头信息 */ public void set(String key, String value) { this.REQUEST_HEADER_HOLDER.get().put(key, value); } public void clear() { this.REQUEST_HEADER_HOLDER.remove(); } /** * 静态内部类的单例模式 */ private static class SingletonHolder { private static final RequestHeaderContextHolder instance = new RequestHeaderContextHolder(); private SingletonHolder() { } } }
|
修改请求拦截器
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class UserTokenHandlerInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String userId = request.getHeader(SecurityConstants.USER_ID_HEADER); String userName = request.getHeader(SecurityConstants.USER_NAME_HEADER); RequestHeaderContextHolder.getInstance().setCurrentUser(userId, userName); return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { RequestHeaderContextHolder.getInstance().clear(); }
}
|
小结:
通过实现requestInterceotor接口我们在每次进行feign调用时都会将header头文件进行传递。但是在异步的环境中无法获取到header头文件信息,我们通过自定义InheritableThreadLocal结构体对header进行保存然后贡献的形式进行处理。这样可以在不同的场景下依然可以完成数据的保存