背景
自定义@CurrentUser
注解想实现当前已登录的用户对象在各层之间进行数据交互,在简书上有一篇比较出名的解决方法:通过自定义@CurrentUser
获取当前登录用户
但是在安全框架Shiro中,通过webRequest.getAttribute("currentUser", RequestAttributes.SCOPE_REQUEST)
却并不可行,👴也不⑧知道什么原因,也是按照该篇文章通过在登陆的业务中通过HttpServletRequest的request.setAttribute()
方法存入需要的信息。
分析
通过对下面的一段覆写代码,可以看出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
public class CurrentUserMethodArgumentResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.getParameterType().isAssignableFrom(User.class) && parameter.hasParameterAnnotation(CurrentUser.class); }
@Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { User user = (User) webRequest.getAttribute("currentUser", RequestAttributes.SCOPE_REQUEST); if (user != null) { return user; } throw new MissingServletRequestPartException("currentUser"); } }
|
绑定了该注解@CurrentUser
的解析器是通过实现HandlerMethodArgumentResolver接口,然后通过webRequest
对象获取之前在request
作用域中的currentUser
。
那么这个NativeWebRequest
是如何得到这个值的呢?我们打开它的源码,发现WebRequest是Spring框架提供的统一请求访问接口,不仅仅可以访问请求相关数据(如参数区数据、请求头数据,但访问不到Cookie区数据),还可以访问会话和上下文中的数据;NativeWebRequest
继承了WebRequest
,并提供访问本地Servlet API的方法。
1 2 3 4 5 6 7 8
| public interface RequestAttributes { int SCOPE_REQUEST = 0; int SCOPE_SESSION = 1; String REFERENCE_REQUEST = "request"; String REFERENCE_SESSION = "session";
@Nullable Object getAttribute(String var1, int var2);
|
而ServletRequestAttributes
的方法则是getAttribute()
的实现,通过对scope的不同来控制作用域。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| private final HttpServletRequest request; @Nullable private volatile HttpSession session; private final Map<String, Object> sessionAttributesToUpdate;
public Object getAttribute(String name, int scope) { if (scope == 0) { if (!this.isRequestActive()) { throw new IllegalStateException("Cannot ask for request attribute - request is not active anymore!"); } else { return this.request.getAttribute(name); } } else { HttpSession session = this.getSession(false); if (session != null) { try { Object value = session.getAttribute(name); if (value != null) { this.sessionAttributesToUpdate.put(name, value); }
return value; } catch (IllegalStateException var5) { } }
return null; } }
|
- 当scope为
RequestAttributes.SCOPE_REQUEST
的时候getAttribute(name)
方法会返回当前线程的HttpServletRequest
的对象的getAttribute(name)
的值。
- 当scope为
RequestAttributes.SCOPE_REQUEST
时会把session
对象的getAttribute(name)
的value存入Map<String, Object> sessionAttributesToUpdate
中。
既然我之前没有从HttpServletRequest
作用域中得到我想要的结果,那么为什么不试试利用session呢。我们可以在登陆的业务中将当前已登录的用户的信息存入session中。
1
| session.setAttribute("currentUser", currentUserDTO/token/id);
|
可以是当前登录对象的数据传输对象,也可以是token或者id。
1 2 3 4
| @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { return webRequest.getAttribute("currentUser", RequestAttributes.SCOPE_SESSION); }
|