背景
自定义 @CurrentUser
注解想实现当前已登录的用户对象在各层之间进行数据交互,在简书上有一篇比较出名的解决方法:通过自定义 @CurrentUser
获取当前登录用户
但是在安全框架 Shiro 中,通过 webRequest.getAttribute("currentUser", RequestAttributes.SCOPE_REQUEST)
却并不可行,👴 也不 ⑧ 知道什么原因,也是按照该篇文章通过在登陆的业务中通过 HttpServletRequest 的 request.setAttribute()
方法存入需要的信息。
分析
通过对下面的一段覆写代码,可以看出:
/**
* 增加方法注入,将含有 @CurrentUser 注解的方法参数注入当前登录用户
*/
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 的方法。
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 的不同来控制作用域。
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 中。
session.setAttribute("currentUser", currentUserDTO/token/id);
可以是当前登录对象的数据传输对象,也可以是 token 或者 id。
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
return webRequest.getAttribute("currentUser", RequestAttributes.SCOPE_SESSION);
}
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于