一.序言
项目接口中经常会出现获取操作人信息的场景,简单的实现方案通常是从单个接口的HttpServletReuqest中获取解析,比较繁琐。今天在一个新的项目中遇到了,便想着用全局的上下文解决。以下作具体探讨和实现。
二.方案概述
a. 通过HttpServletRequest获取
@PostMapping("/parseHeader")
public String messageLog(HttpServletRequest request){
String token = request.getHeader("token");
return getUserNameByToken(token);
}
b. 通过@RequestHeader解析
@PostMapping("/parseHeader")
public String messageLog(@RequestHeader String token){
return getUserNameByToken(token);
}
c. 构件全局Http上下文,接口中调用
@PostMapping("/parseHeader")
public String messageLog(){
String userName = AppContextUtil.getUserName();
return userName;
}
前两种方案在有新的请求头增加时,需要在每一处频繁改动方法签名,而第三种方案只需要在Http上下文中增加解析结果参数,便可以在所有地方使用,比较灵活,代码实现也更简洁。
三.代码实现(第三种解决方案)
-
controller层
@PostMapping("/parseHeader") public String messageLog(){ String userName = AppContextUtil.getUserName(); return userName; }
-
Appcontext(Http上下文)
@Data public class AppContext { /** * 身份令牌 */ private String token; /** * 用户Id */ private String userId; /** * 用户名称 */ private String userName; /** * 请求IP */ private String ip; }
-
拦截器
//方便下文注入时获取 public class PmsInterceptor extends HandlerInterceptorAdapter { } @Component @Slf4j public class AppContextInterceptor extends PmsInterceptor{ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { AppContext appContext = new AppContext(); String token = request.getHeader("token"); appContext.setToken(token); appContext.setIp(RequestUtil.getRealIp()); if(StringUtils.isNotEmpty(token)){ UserInfo user = getUserFromToken(token); if(user != null){ appContext.setUserId(user.getUserId()); appContext.setUserName(user.getUserName()); } } request.setAttribute(Constants.APP_CONTEXT, appContext); return super.preHandle(request, response, handler); } public UserInfo getUserFromToken(String token){ //此处添加自己的实现,如从token解密后从数据库中查询用户信息 UserInfo userInfo = new UserInfo(); userInfo.setUserId("666"); userInfo.setUserName("Ciao"); return userInfo; } }
-
MvcConfig拦截器注册(否则拦截器不生效)
@Configuration @Slf4j public class MvcConfig implements WebMvcConfigurer { @Resource List<PmsInterceptor> pmsInterceptorList; @Override public void addInterceptors(InterceptorRegistry registry) { if(CollectionUtils.isEmpty(pmsInterceptorList)){ return; } for(PmsInterceptor pmsInterceptor : pmsInterceptorList){ log.info("添加拦截器:{}", pmsInterceptor); registry.addInterceptor(pmsInterceptor); } WebMvcConfigurer.super.addInterceptors(registry); } }
-
RequestUtil请求工具类
@Slf4j public class RequestUtil { private RequestUtil(){ } public static HttpServletRequest getCurrentHttpRequest() { return getCurrentHttpRequest(true); } public static HttpServletRequest getCurrentHttpRequest(boolean warnlog){ RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes(); if(requestAttributes instanceof ServletRequestAttributes){ return ((ServletRequestAttributes) requestAttributes).getRequest(); }else{ if(warnlog){ log.warn("not called in the context of attribute"); } return null; } } ...可以自行补充其他方法,如:IP、UserAgent、Host }
-
AppContextUtil上下文获取工具类
public class AppContextUtil { private AppContextUtil(){ } public static AppContext getAppContext(){ HttpServletRequest request = RequestUtil.getCurrentHttpRequest(); if(request == null){ return new AppContext(); } Object attribute = request.getAttribute(Constants.APP_CONTEXT); return attribute == null? new AppContext() : (AppContext) attribute; } public static String getUserName(){ return getAppContext().getUserName(); } public static String getToken(){ return getAppContext().getToken(); } public static String getUserId(){ return getAppContext().getUserId(); } }
后续会展开讲解springmvc的调用链路,对拦截器和过滤器的使用场景和区别作详细说明。