實現思路:
- 准備一個ThreadLocal變量,供線程之間共享。
- 每個微服對請求過濾,不管是經過網關的請求還是Feign的請求,如果是從網關過來的請求,從請求頭中獲取
token
並解析得到用戶信息,然后存入ThreadLocal
變量;如果是feign請求,直接獲取請求頭中的用戶信息存入ThreadLocal
中。 - 每個微服務在使用Feign調用別的微服務時,先從ThreadLocal里面取出user信息,並放在request的請求頭中。
ThreadLocal工具類:
package com.orange.common.system;
import com.orange.common.entity.UserInfo;
public class UserInfoContext {
private static ThreadLocal<UserInfo> userInfo = new ThreadLocal<>();
public final static String FEIGN_HEADER_USERINFO = "X-FEIGN-HEADER-USERINFO";
public static UserInfo getUser() {
return (UserInfo) userInfo.get();
}
public static void setUser(UserInfo user) {
userInfo.set(user);
}
}
承載用戶信息的實體類:
package com.orange.common.entity;
import lombok.Data;
import java.io.Serializable;
@Data
public class UserInfo implements Serializable {
private Long userId;
private String username;
}
微服務的請求過濾器
package com.orange.provider.component;
import com.alibaba.fastjson.JSON;
import com.orange.common.entity.UserInfo;
import com.orange.common.system.UserInfoContext;
import com.orange.common.util.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@Slf4j
@Component
@Order(1)
public class UserInfoFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
initUserInfo((HttpServletRequest) servletRequest);
filterChain.doFilter(servletRequest, servletResponse);
}
private void initUserInfo(HttpServletRequest request) {
log.info("執行過濾器{}===================", "UserInfoFilter");
String token = request.getHeader(JwtUtil.HEADER_TOKEN);
log.info("獲取請求頭中的token:{}=======", token);
String username = JwtUtil.getUsername(token);
if (username != null) {
UserInfo userInfo = new UserInfo();
userInfo.setUsername(username);
userInfo.setUserId(JwtUtil.getUserId(token));
UserInfoContext.setUser(userInfo);
}
String userJson = request.getHeader(UserInfoContext.FEIGN_HEADER_USERINFO);
log.info("獲取feign請求頭中的userInfo: {}=========", userJson);
if (!StringUtils.isEmpty(userJson)) {
UserInfo userInfo = JSON.parseObject(userJson, UserInfo.class);
UserInfoContext.setUser(userInfo);
}
}
}
Feign請求攔截器
package com.orange.consumer.config;
import com.alibaba.fastjson.JSON;
import com.orange.common.entity.UserInfo;
import com.orange.common.system.UserInfoContext;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
@Slf4j
@Configuration
public class FeignConfig implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
log.info("feign請求攔截器============");
//從應用上下文中取出user信息,放入Feign的請求頭中
UserInfo user = UserInfoContext.getUser();
log.info("應用上下文中的UserInfo:{}",user);
if (user != null) {
String userJson = JSON.toJSONString(user);
try {
requestTemplate.header(UserInfoContext.FEIGN_HEADER_USERINFO, userJson);
} catch (Exception e) {
log.info("用戶信息設置失敗=========");
}
}
}
}