公司的服务需要拆分了,调研过后采用决定采用dubbo进行分布式服务拆分,在分布式的环境下,用户信息或者链路日志id需要在不同分布式服务之间传递,这里记录下实现方式:
首先导入依赖,pom如下
org.apache.dubbo
dubbo-spring-boot-starter
2.7.12
org.springframework.boot
spring-boot-starter-web
2.3.0.RELEASE
org.projectlombok
lombok
com.alibaba
fastjson
1.2.70
其次创建全局用户对象
package com.example.demo.entity;
import lombok.Data;
import java.io.Serializable;
/**
* 登录用户对象
*
* @author charles
*/
@Data
public class LoginUser implements Serializable {
private static final long serialVersionUID = 1599282604110237521L;
/**
* 用户id
*/
private Integer id;
/**
* 用户号码
*/
private String userTel;
/**
* 用户名
*/
private String userName;
}
创建保存用户信息对象的上下文
import com.example.demo.context;
import com.example.demo.entity.LoginUser;
public class UserContext {
private static ThreadLocal userHolder = new ThreadLocal();
public static void setUser(LoginUser loginUser) {
userHolder.set(loginUser);
}
public static LoginUser getUser() {
return userHolder.get();
}
public static void clear() {
userHolder.remove();
}
}
创建请求拦截器,获取用户信息填充进入上下文,这里我使用的是自己的方法,可以根据个人不同的情况进行调整
package com.example.demo.handle.interceptor;
import com.alibaba.fastjson.JSONObject;
import com.example.demo.context.UserContext;
import com.example.demo.entity.LoginUser;
import com.example.demo.util.TicketUtil;
import com.example.demo.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import redis.clients.jedis.JedisPool;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 拦截请求,获取用户信息填充上下文
*/
@Slf4j
public class UserInterceptor extends HandlerInterceptorAdapter {
@Resource
private JedisPool jedisPool;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
LoginUser user = new LoginUser();
//注意这里是我自己的获取用户信息的方式,可以根据个人不同的情况进行调整
// 获取ticket
String ticket = TicketUtil.getTicketValue(request);
// 不需要强制登录, 取当前用户信息有就返回无则不返回 不进行拦截
if (StringUtils.isNotBlank(ticket)) {
String jsonUserBo = RedisUtil.get(jedisPool, ticket);
if (StringUtils.isNotBlank(jsonUserBo)) {
LoginUser tmpUserBo = JSONObject.parseObject(jsonUserBo, LoginUser.class);
BeanUtils.copyProperties(tmpUserBo, user);
}
}
UserContext.setUser(user);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
UserContext.clear();
}
}
拦截器生效
package com.example.demo.config;
import com.example.demo.interceptor.UserInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author charles
*/
@Slf4j
@Configuration
public class UserInfoConfiguration implements WebMvcConfigurer {
@Bean
public UserInterceptor userInterceptor() {
return new UserInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 拦截器
registry.addInterceptor(this.userInterceptor());
}
}
接下来就是重点了,dubbo拦截器获取用户信息
首先是消费方拦截器
package com.example.demo.filter;
import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.rpc.*;
import com.alibaba.fastjson.JSON;
import com.example.demo.context.UserContext;
import com.example.demo.entity.LoginUser;
import com.example.demo.constants.Constant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@Slf4j
@Activate(group = {Constant.CONSUMER})
public class UserInfoConsumerFilter implements Filter {
@Override
public Result invoke(Invoker> invoker, Invocation invocation) throws RpcException {
LoginUser user = UserContext.getUser();
String userInfo;
if (user != null) {
userInfo = JSON.toJSONString(user);
} else {
userInfo = RpcContext.getContext().getAttachment(Constant.DUBBO_USER_KEY);
}
invocation.getAttachments().put(Constant.DUBBO_USER_KEY, StringUtils.isBlank(userInfo) ? JSON.toJSONString(new LoginUser()) : userInfo);
return invoker.invoke(invocation);
}
}
再然后是提供方拦截器
package com.example.demo.filter;
import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.rpc.*;
import com.alibaba.fastjson.JSON;
import com.example.demo.context.UserContext;
import com.example.demo.entity.LoginUser;
import com.example.demo.constants.Constant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@Slf4j
@Activate(group = {Constant.PROVIDER})
public class UserInfoProviderFilter implements Filter {
@Override
public Result invoke(Invoker> invoker, Invocation invocation) throws RpcException {
String userInfo = RpcContext.getContext().getAttachment(Constant.DUBBO_USER_KEY);
if (StringUtils.isBlank(userInfo)) {
return invoker.invoke(invocation);
}
try {
LoginUser user = JSON.parseObject(userInfo, LoginUser.class);
UserContext.setUser(user);
return invoker.invoke(invocation);
} finally {
UserContext.clear();
}
}
}
最后最最重要的一部,添加拦截器生效配置
userInfoConsumerFilter=com.example.demo.filter.UserInfoConsumerFilter
userInfoProviderFilter=com.example.demo.filter.UserInfoProviderFilter