根据 token 获取用户信息 
Level1: 手动获取 
通常 token 会放在 header 当中,最低级的获取方式就是直接从 header 中获取 token,然后通过 token 转换获得 userId,示例代码如下:
@GetMapping("/level1")
public Integer level1(HttpServletRequest request) {
    String token = request.getHeader("token");
    log.info("level1 获得的token为:{}", token);
    Integer userId = TokenUtil.getUserIdByToken(token);
    log.info("userId={}", userId);
    return userId;
}这种方式最简单直观,还可以进一步封装,比如提供一个 BaseController,封装公共的部分,本质是一样的,但又引入了继承关系。因此,通常适用于有少数地方使用的场景。如果有大量的地方使用,这样写比较麻烦,** **,也没什么技术含量。
Level2:过滤器 token 转 userId 
在上一种方案中,既然每一次调用都需要进行 token 和 userId 的转换,那就通过过滤器将这一转换过程统一处理。在过滤器中获得 token,然后转换成 userId,再把 userId 写回到 header 当中,使用时直接从 header 中拿 userId 即可。
先定义过滤器,示例代码如下
@Slf4j
@Component
public class ArgumentFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        String token = httpRequest.getHeader("token");
        Integer userId = TokenUtil.getUserIdByToken(token);
        log.info("filter获取用户Id={}", userId);
        HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(httpRequest) {
            @Override
            public String getHeader(String name) {
                if ("userId".equals(name)) {
                    return userId + "";
                }
                return super.getHeader(name);
            }
        };
        filterChain.doFilter(requestWrapper, httpResponse);
    }
}这里主要通过实现 Filter 接口的 doFilter 方法(JDK8 可用实现需要的接口方法即可),在 request 中获得 token 之后,通过 HttpServletRequestWrapper 将转换之后的 userId 放置在 header 当中。
虽然这种方式已经进步了很多,但每次都要获得 HttpServletRequest,然后再从其中获得 userId,还是有一些不方便
Level3:参数匹配 
上一种方式已经处理获得了 userId,那么能不能做的更彻底一些,只需要在 Controller 方法上出现 userId,就直接给它赋值呢?
这里从 header 中获取到 token,转换为 userId,然后匹配方法的参数名称,如果是 userId,那么就将转换之后的 userId 赋值给对应的参数。
至此,是不是就完美了?好像还有一些瑕疵。
第一个:虽然按照约定定义 userId 参数即可,但容易误伤,比如某些业务有自身的 userId,不小心命名重复了,会有被覆盖的风险。
第二个:参数的名称只能是 userId,且不能够灵活的定义其他名称。
第三个:如果想返回更多信息,比如用户(User)的信息,处理就变得更加复杂。而且如果 body 体传递的参数比较复杂,解析成 Map 再封装转换有一定的风险和性能问题。
Level4:方法参数解析器 
Spring 提供了多种解析器 Resolver,比如常用的统一处理异常的 HandlerExceptionResolver。同时,还提供了用来处理方法参数的解析器 HandlerMethodArgumentResolver。它包含 2 个方法:supportsParameter 和 resolveArgument。其中前者用来判断是否满足某个条件,当满足条件(返回 true)则可进入 resolveArgument 方法进行具体处理操作。
基于 HandlerExceptionResolver,我们可以分以下部分来进行实现:
自定义注解@CurrentUser,用于 Controller 方法上的 User 参数; 自定义 LoginUserHandlerMethodArgumentResolver,实现 HandlerMethodArgumentResolver 接口,通过 supportsParameter 检查符合条件的参数,通过 resolveArgument 方法来将 token 转换成 User 对象,并赋值给参数。 注册 HandlerMethodArgumentResolver 到 MVC 当中。