JAVA技术 十月 29, 2019

@CurrentUser注解新配方

文章字数 7.6k 阅读约需 7 mins. 阅读次数 0

背景

自定义@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);
}
0%