场景

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<HandlerMethodArgumentResolver> argumentResolvers = adapter.getArgumentResolvers();
            //添加自定义参数处理器
            argumentResolvers = addArgumentResolvers(argumentResolvers);
            adapter.setArgumentResolvers(argumentResolvers);
        }
        return null;
    }

    private List<HandlerMethodArgumentResolver> addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        List<HandlerMethodArgumentResolver> 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;
    }
}

哒哒队长
7 声望4 粉丝