Springboot 用戶參數自動注入

場景

web應用中常會用到用戶信息,通常都是從會話中獲取,不過這樣不優雅,類似的代碼處處都是,但願能夠經過spring的特性,在我須要的時候能夠自動裝配。web

思路

1.登陸時產生token,同時把token與反序列化的用戶信息放入redis中;
2.訪問時攜帶token,網關層進行攔截,同時從redis經過token將用戶信息放入header中;
3.業務層,經過攔截器攔截請求,從header中獲取反序列化的用戶信息,放入Request中;
4.經過參數分解器,從Request中獲取參數注入redis

參考資料

HandlerMethodArgumentResolver:https://www.jianshu.com/p/ac9...spring

BeanPostProcessor:https://blog.csdn.net/zhyh198...json

BeanPostProcessor和BeanFactoryProcessor淺析:https://www.jianshu.com/p/fb3...app

關鍵代碼

網關層

過濾器

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;
    }
}
相關文章
相關標籤/搜索