dubbo 使用过滤器(filter)上下文传播用户信息
目录
1背景
在使用springboot + dubbo 微服务模式下,有多个微服务互相调用,有一些统一的数据需要获取,比如用户id等。
比如一个用户财务账号到账后需要调用信息服务通知用户,像用户id每个接口都需要,不需要作为方法参数显式传递,可以做统一处理隐式传参。
2.如何实现
2.1整体处理流程
用户请求在网关处被 filter
拦截,获取请求的 token
查询到 UserId
,将 UserId
设置到 http请求header中。
在调用下游服务时 dubbo filter
拦截请求将http header中的 UserId
设置到rpc请求的附属信息中。
下游服务从rpc请求的附属请求中获取到 UserId
。
2.2 实现细节
//网关拦截
@Slf4j
@WebFilter(filterName = "accessFilter", urlPatterns = {"/api/*"})
public class AccessFilter implements Filter {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String token = request.getHeader(Constants.TOKEN);
String json = stringRedisTemplate.opsForValue().get(token);
User user = JSON.parseObject(json, User.class);
MutableHttpServletRequest mutableRequest = new MutableHttpServletRequest(request);
mutableRequest.putHeader(Constants.USER_ID, user.getId());
chain.doFilter(mutableRequest, servletResponse);
}
}
//包装http请求
public final class MutableHttpServletRequest extends HttpServletRequestWrapper {
// holds custom header and value mapping
private final Map<String, String> customHeaders;
public MutableHttpServletRequest(HttpServletRequest request){
super(request);
this.customHeaders = new HashMap<String, String>();
}
public void putHeader(String name, String value){
this.customHeaders.put(name, value);
}
public String getHeader(String name) {
// check the custom headers first
String headerValue = customHeaders.get(name);
if (headerValue != null){
return headerValue;
}
// else return from into the original wrapped object
return ((HttpServletRequest) getRequest()).getHeader(name);
}
public Enumeration<String> getHeaderNames() {
// create a set of the custom header names
Set<String> set = new HashSet<String>(customHeaders.keySet());
// now add the headers from the wrapped request object
@SuppressWarnings("unchecked")
Enumeration<String> e = ((HttpServletRequest) getRequest()).getHeaderNames();
while (e.hasMoreElements()) {
// add the names of the request headers into the list
String n = e.nextElement();
set.add(n);
}
// create an enumeration from the set and return
return Collections.enumeration(set);
}
}
//dubbo添加filter,如何实现请百度
@Slf4j
@Activate(group = CONSUMER)
public class ContextConsumerFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
RpcContext.getContext().setAttachment(Constants.USER_ID, AuthUtils.getUserId(Constants.USER_ID));
return invoker.invoke(invocation);
}
}
//封装如何获取当前用户id工具类
@Slf4j
@Component
public class AuthUtils {
private static AuthUtils authUtils;
@PostConstruct
public void init() {
authUtils = this;
}
public static Long getUserId() {
Long userId = getContextId(Constants.USER_ID);
if (userId == null) {
throw new ServiceException("B0001", "获取上下文 userId 失败");
}
return userId;
}
/**
* 获取当前请求 request
*/
public static HttpServletRequest getCurrentHttpRequest() {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes instanceof ServletRequestAttributes) {
return ((ServletRequestAttributes) requestAttributes).getRequest();
}
return null;
}
/**
* 从上下文中获取传递的ID
*
* @param key
* @return
*/
public static Long getContextId(String key) {
HttpServletRequest request = getCurrentHttpRequest();
if (request != null) {
//web请求
String contextId = request.getHeader(key);
if (StringUtils.isNotEmpty(contextId)) {
return Long.parseLong(contextId);
}
} else {
Long contextId = (Long) RpcContext.getContext().getObjectAttachment(key);
if (contextId != null) {
return contextId;
}
}
return null;
}
}
//当前服务和下游服务都可以使用 AuthUtils.getUserId()直接获取用户od
touch fish