本文中介绍我在简单的实际应用中,针对登录用户的访问权限问题的具体实现,整体设计如下图:
图中请求进入Spring容器后会对用户的有效性、权限进行验证,只有验证都通过之后才能进入实际业务逻辑。
上图的实现使用Spring MVC的HandleInterceptor、HandlerInterceptorAdapter实现,下文中将一一介绍到。
HandlerInterceptor接口需要实现类管理请求执行前、执行后、和请求处理完成后所需要执行的动作。代码如下:
public interface HandlerInterceptor {
boolean preHandle(HttpServletRequest var1,HttpServletResponse var2, Object var3) throws Exception;
void postHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3, ModelAndView var4) throws Exception;
void afterCompletion(HttpServletRequest var1, HttpServletResponse var2, Object var3, Exception var4) throws Exception;
}
preHandle
:对HTTP请求进行预处理,返回true则请求继续,返回false则需要对Response进行响应postHandle
:在HTTP请求实际业务逻辑完成后(View渲染之前),处理需要做的动作afterCompletion
:在View渲染完成之后,处理需要的动作
如果操作都需要进行控制,可以直接继承该接口并一一实现其中的方法。但有些时候我们并不需要实现这么多的操作,例如:现在我要对访问的路径进行校验,则我只需要实现preHandle
方法就可以满足,这里要用到它的适配器HandlerInterceptorAdapter
,代码如下:
public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {
public HandlerInterceptorAdapter() {
}
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
}
}
afterConcurrentHandlingStarted
:该适配器中还对当前拦截器开始执行后的动作进行了拦截处理,适用于在进行验证之前对HTTP请求做一些统一的处理
接着,我们继承该适配器并重写其中的方法就可以达到我们的目的了,在这里以我的权限检查程序片段为例,代码如下:
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 从请求中拿到token
String account = request.getParameter(ACCOUNT_KEY);
String token = request.getParameter(TOKEN_KEY);
// 打印Token和请求地址
LOGGER.info("Account------->{}", account);
LOGGER.info("Token--------->{}", token);
LOGGER.info("ReqUrl-------->{}", request.getRequestURI());
// 任意验证属性为空->重新登录
if (StringUtils.isEmpty(account) || StringUtils.isEmpty(token)) {
response.sendRedirect(request.getContextPath() + "/err/needLogin");
return false;
}
// 验证用户有效性.
boolean flag = validateAccount(request, account, token);
if (!flag) {
// 用户权限验证未通过->重新登录
response.sendRedirect(request.getContextPath() + "/err/needLogin");
return false;
}
// 验证路径权限(AntPathMatcher实现)
boolean authFlag = authPath(request, account);
// 打印验证结果
LOGGER.info("UserAuth----->{}", flag);
LOGGER.info("PathAuth----->{}", authFlag);
if (flag && authFlag) {
return true;
} else {
// 未通过路径权限验证,跳转到错误URL
response.sendRedirect(request.getContextPath() + "/err/notAllow");
}
return false;
}
这里根据我的验证逻辑我只实现了preHandle
方法,其中使用AntPathMatcher实现路径匹配的实现会在下一篇中详细介绍。实现类完成后需要配置生效,那么在spring的配置文件中,我们可以这么写,代码如下:
<!--Spring 路径拦截器配置-->
<mvc:interceptors>
<mvc:interceptor>
<!--匹配路径-->
<mvc:mapping path="/**"/>
<!--从匹配路径移除不需要匹配的路径-->
<mvc:exclude-mapping path="/user/login"/>
<mvc:exclude-mapping path="/user/secode"/>
<mvc:exclude-mapping path="/user/passwd"/>
<mvc:exclude-mapping path="/err/*"/>
<mvc:exclude-mapping path="/news/**"/>
<!--该拦截器的实现(上面我们完成的类)-->
<bean class="cn.creditease.app.util.PathInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
不要忘了引入Spring Schema:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
这样我们就通过Spring的拦截器实现了URL访问权限校验。