场景
web应用中常会用到用户信息,一般都是从会话中获取,不过这样不优雅,相似的代码到处都是,希望可以通过spring的特性,在我需要的时候可以自动装配。
思路
1.登录时产生token,同时把token与反序列化的用户信息放入redis中;
2.访问时携带token,网关层进行拦截,同时从redis通过token将用户信息放入header中;
3.业务层,通过拦截器拦截请求,从header中获取反序列化的用户信息,放入Request中;
4.通过参数分解器,从Request中获取参数注入
参考资料
HandlerMethodArgumentResolver:https://www.jianshu.com/p/ac9...
BeanPostProcessor:https://blog.csdn.net/zhyh198...
BeanPostProcessor和BeanFactoryProcessor浅析:https://www.jianshu.com/p/fb3...
关键代码
网关层
过滤器
public class UserResolveFilter extends ZuulFilter {
/**
* 请求拦截类型
*
* @return
*/
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return 20;
}
/**
* 哪些请求要被拦截
*
* @return
*/
@Override
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
// 端口号之后的请求地址
return request.getRequestURI().startsWith("/core-server");
}
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
ctx.getZuulRequestHeaders().remove("user");
HttpServletRequest request = ctx.getRequest();
String token = request.getHeader("token");
if (!StringUtils.isBlank(token)) {
// 这里获取token后,可以完成自己的映射操作
ctx.addZuulRequestHeader("user", "{\"id\":1,\"name\":\"jiaotd\",\"age\":18}");
}
return null;
}
}
添加配置
@Configuration
@Slf4j
public class Config {
@Bean
public UserResolveFilter userCenterResolveUserFilter() {
log.info("init UserResolveFilter");
return new UserResolveFilter();
}
}
业务层
过滤器
@Configuration
@WebFilter(filterName = "userInfoResolverFilter", urlPatterns = "/*")
public class UserInfoResolverFilter implements Filter {
//@WebFilter 用于将一个类声明为过滤器,该注解将会在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为过滤器
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("UserInfoResolverFilter init().....");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
String json = ((HttpServletRequest) servletRequest).getHeader("user");
log.info("请求头中的User为:{}", json);
User user = JSON.parseObject(URLDecoder.decode(json, "utf-8"), User.class);
servletRequest.setAttribute("user", user);
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
log.info("UserInfoResolverFilter destroy().....");
}
}
参数分解器
public class UserArgumentResolver implements HandlerMethodArgumentResolver {
/**
* 参数类型是User的执行 #resolveArgument 方法
*
* @param methodParameter
* @return
*/
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.getParameterType() == User.class;
}
/**
* 赋值
*
* @param methodParameter
* @param modelAndViewContainer
* @param nativeWebRequest
* @param webDataBinderFactory
* @return
* @throws Exception
*/
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
User user = (User) nativeWebRequest.getAttribute("user", RequestAttributes.SCOPE_REQUEST);
return user;
}
}
将自定义参数分解器加入spring bean 初始化流程中
@Component
public class ArgumentResolverPostProcessor implements BeanPostProcessor {
/**
* spring 初始化 Bean 操作
*
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof RequestMappingHandlerAdapter) {
//requestMappingHandlerAdapter进行修改
RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter) bean;
List argumentResolvers = adapter.getArgumentResolvers();
//添加自定义参数处理器
argumentResolvers = addArgumentResolvers(argumentResolvers);
adapter.setArgumentResolvers(argumentResolvers);
}
return null;
}
private List addArgumentResolvers(List argumentResolvers) {
List resolvers = new ArrayList<>();
//将自定的添加到最前面
resolvers.add(new UserArgumentResolver());
//将原本的添加后面
resolvers.addAll(argumentResolvers);
return resolvers;
}
使用
@RestController
@RequestMapping("/core")
public class CoreController {
@GetMapping("/user")
public User getUser(User user) {
return user;
}
}