作者:A稻田守望者,Java工程师一枚,热爱开源技术,架构师社区合伙人!
上周末拜读了一位牛人的公众号文章<>,语言风趣,引人入胜,
回顾一下文章内容,为了在Controller
的方法中获取已经认证过的用户信息(比如通过JWT-JSON Web Token传输的Token
),文中提供了三种方式:
方式一(很挫)直接在
Controller
方法中获取Token
头,然后解析;方式二(优雅)在过滤器
Filter
中验证JWT
后,直接使用HttpServletRequestWrapper
偷梁换柱,覆盖getHeader
方法,然后在Controller
方法中调用getHeader
,这样就不需要再次解析了;方式三(很优雅)同样在过滤器
Filter
中使用HttpServletRequestWrapper
,只是覆盖getParameterNames
、getParameterValues
(针对表单提交)和getInputStream
(针对JSON提交),然后就可以和客户端参数相同的方式获取了。
方式一需要重复解析JWT
,而且控制器和Servlet API
绑定,不方便测试,但是胜在简单直接。方式二和方式三虽然是一个很好的练习HttpServletRequestWrapper
的示例,但是可能还算不上是优雅的获取用户信息的方式。
不妨思考一下:
除了获取
userId
外,如果还想获取JWT中PAYLOAD
的其它信息,能不能做到只修改Controller
?还是需要再次修改验证JWT
的过滤器Filter
呢?HttpServletRequest
的getInpustStream()
方法,Web容器实现基本都是只能调用一次的,因而方式三在扩展getInpustStream()
的时候,先将其转换为byte[]
,然后为了添加用户信息,再将byte[]
反序列化为map
,添加用户信息之后又序列化为byte[]
,反复多次,这种方式性能怎么样?如果是文件上传,这种方式能否行得通?方式三中
HttpServletRequestWrapper
会无形中启到屏蔽loginUserId
参数的作用,但如果客户端的的确确传入了一个loginUserId
的参数(当然,这种情况还是需要尽量避免),在Controller
中怎么又获取到客户端的这个参数?
有没有什么其它的方式呢?
SpringMVC
中关于参数绑定有很多接口,其中很关键的一个是HandlerMethodArgumentResolver
,可以通过添加新实现类来实现获取用户信息吗?当然可以,对应该接口的两个方法,首先要能够识别什么情况下需要绑定用户信息,一般来说,可以根据参数的特殊类型,也可以根据参数的特殊注解;其次要能够获取到用户信息,类似于原文中做的那样。虽然这样做也可以实现功能,但是却很繁琐。
不如抛开怎么获取用户信息不谈,先来看看SpringMVC
在控制器的处理方法HandlerMethod
中绑定参数是怎么做的?
熟悉SpringMVC
处理流程的朋友,自然知道,主控制器是DispatcherServlet
,在doDispatch()
方法中根据HandlerMapping
找到处理器,然后找到可以调用该处理器的HandlerAdapter
,其中最常用也最核心的莫过于RequestMappingHandlerMapping
、HandlerMethod
、RequestMappingHandlerAdapter
组合了。查看RequestMappingHandlerAdapter
的源码,找到调用HandlerMethod
的方法:
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {