1. 自定义拦截器
所谓的拦截器,就是用来拦截请求,因此我们可以对用户发来的请求做处理。
- 写一个类,实现拦截器的接口
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class FirstInterceptor implements HandlerInterceptor{
/**
* 该方法在 目标方法之前被调用
* 可以用来做 权限,日志,事务
* 若 return false,后续的拦截器和目标方法将不被调用
* 若 return true,后续的拦截器和目标方法正常调用
*/
@Override
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1,
Object arg2) throws Exception {
System.out.println("preHandle");
return true;
}
/**
* 该方法在 目标方法之后,渲染视图之前 被调用
* 可以用来 修改请求域,修改目标视图等
*/
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
Object arg2, ModelAndView arg3) throws Exception {
System.out.println("postHandle");
}
/**
* 该方法在 目标方法被调用,渲染视图之后被调用
* 可以用来 释放资源
*/
@Override
public void afterCompletion(HttpServletRequest arg0,
HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
System.out.println("afterCompletion");
}
}
代码中已经有了详细的注释,包括三个方法的调用顺序方法
在三个方法中打印出一句话,以便我们能看到效果
让拦截器起作用,就要在springmvc配置文件中配置
<!-- 拦截器 -->
<mvc:interceptors>
<!-- 配置自定义的拦截器 -->
<bean class="com.zj.interceptor.FirstInterceptor"></bean>
</mvc:interceptors>
到这里,配置就完成了,访问任意目标方法,控制台打印出如下结果
2. 配置拦截器的作用范围
<mvc:interceptors>
<!-- 配置自定义的拦截器 -->
<bean class="com.zj.interceptor.FirstInterceptor"></bean>
<mvc:interceptor>
<!-- 只有访问/hello这个路径,拦截器才起作用 -->
<mvc:mapping path="/hello"/>
<!--
除了/index这个路径,其他都起作用
<mvc:exclude-mapping path="/index"/>
-->
<bean class="com.zj.interceptor.SecondInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
在最外层是一个mvc:interceptors,在里面,我们又配置了一个mvc:interceptor。请注意,前者有s,后者没有。
在mvc:interceptor中,我们可以配置拦截器SecondInterceptor的作用范围,只作用于某个路径或者只有某个路径不被作用
3. 多个拦截器的调用顺序
上面我们配了两个拦截器,现在我们来测试以下他们的调用顺序
访问某个路径,控制台打印出如下结果
- 在配置文件中,FirstInterceptor拦截器的配置在SecondInterceptor前面
- 根据结果分析可以得到,preHandle()方法的调用顺序和拦截器的配置顺序一致
- postHandle()和afterCompletion()方法的调用顺序则是反序
4. 特殊情况
刚刚的例子中,FirstInterceptor和SecondInterceptor两个拦截器的preHandler方法都是return true,因此后面的方面都可以正常执行。
现在假设SecondInterceptor的preHandle返回false,会怎样?我们来看结果截图
1. 从结果中看到,FirstInterceptor 的preHandle先被执行,这没问题
2.然后执行SecondInterceptor的preHandle,它返回false,因此后续的目标方法都不被执行,但是,FirstInterceptor的afterCompletion方法居然被执行了。
3.如果从源码分析,可以明白这是为什么,但是对于大多数人来说这太难。
4.我们从这个结果中总结出一个结论:一个拦截器一旦preHandle被执行return true,那么它就一定要释放资源(之前说过afterCompletion方法用来释放资源)
======================================================
根据上面的分析下面我写了一个不使用xml进行拦截路径的案例。
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import com.xxxx.dkyw.moudles.doctorinfo.service.DoctorInfoService;
import com.xxxx.dkyw.moudles.hosinfo.service.HosInfoService;
import com.xxxx.dkyw.moudles.examorg.service.ExamOrgInfoService;
import com.xxxx.dkyw.moudles.organizationinfo.service.OrganizationInfoService;
import com.xxxx.dkyw.core.extensions.process.service.ProcessService;
import com.xxxx.dkyw.persistence.entity.HosInfo;
import com.xxxx.dkyw.persistence.entity.OrganizationInfo;
import com.xxxx.dkyw.persistence.entity.Process;
@Component
public class AdminPermOrgInterceptor implements HandlerInterceptor {
@Autowired
private DoctorInfoService doctorInfoService;
@Autowired
private ProcessService processService;
@Autowired
private HosInfoService hosInfoService;
@Autowired
private OrganizationInfoService organizationInfoService;
@Autowired
private ExamOrgInfoService examOrgInfoService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
@SuppressWarnings("static-access")
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
String absolutePath = request.getScheme()+"://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath();
String url = request.getRequestURL().toString();
if(request.getMethod().toUpperCase().equals("GET")) {
if(StringUtils.containsAny(url, "/doctor/", "/exam/", "/examorg/", "/hos/", "/org/") &&
StringUtils.containsAny(url, "edit", "view")) {
Map<String, Object> model = modelAndView.getModel();
HandlerMethod methodHandler = (HandlerMethod) handler;
Class<?>[] parameterTypes = methodHandler.getMethod().getParameterTypes();
Class<?> beanClass = parameterTypes[0];
String beanName = beanClass.getName();
char[] simpleName = beanClass.getSimpleName().toCharArray();
simpleName[0] += 32;
Object value = model.get(String.valueOf(simpleName));
Class<?> valueClass = value.getClass().forName(beanName);
Field[] fields = valueClass.getDeclaredFields();
String uuid = null;
String orgUuid = null;
List<String> orgUuids = null;
for(Field f: fields) {
f.setAccessible(true);
if(f.getName().equals("uuid")) {
uuid = f.get(value).toString();
break;
}
}
if(StringUtils.contains(url, "/doctor/")) { // 医师信息
// DoctorBaseInfo doctorBaseInfo = doctorInfoService.getDoctorBaseInfoByUuid(uuid);
// orgUuid = doctorBaseInfo.getOrgUuid();
orgUuid = doctorInfoService.getDoctorOrgUuid(uuid);
}else if(StringUtils.contains(url, "/exam/")) { // 考核进度信息
Process process = processService.getProcessByUuid(uuid);
orgUuid = process.getOrgUuid();
}else if(StringUtils.contains(url, "/hos/")) { // 卫生机构信息
HosInfo hosInfo = hosInfoService.getHosInfoByUuid(uuid);
orgUuid = hosInfo.getOrgUuid();
}else if(StringUtils.contains(url, "/org/")) { // 行政机构信息
OrganizationInfo orgInfo = organizationInfoService.getOrgInfoByUuid(uuid);
orgUuid = orgInfo.getFirstUuid();
}else { // 考核机构信息
orgUuids = examOrgInfoService.getBelongOrgIdByUuid(uuid);
}
List<String> permOrgList = (List<String>) request.getSession().getAttribute("SESSION_ORG_LIST");
if(CollectionUtils.isNotEmpty(permOrgList)) {
if(StringUtils.isNotEmpty(orgUuid)) {
OrganizationInfo org = organizationInfoService.findOrgFirstUuid(orgUuid);
orgUuid = org.getUuid();
if(!permOrgList.contains(orgUuid)) { // 当前用户的机构权限无法查看用户的情况
// response.sendError(HttpServletResponse.SC_FORBIDDEN, "没有权限的操作。");
response.sendRedirect(absolutePath + "/a/forbidden");
}
}else if(CollectionUtils.isNotEmpty(orgUuids)) {
List<OrganizationInfo> orgs = organizationInfoService.queryOrgsByUuidList(orgUuids);
for(OrganizationInfo o: orgs) {
if(!permOrgList.contains(o.getFirstUuid())) {
response.sendRedirect(absolutePath + "/a/forbidden");
}
}
}else {
response.sendRedirect(absolutePath + "/a/forbidden");
}
}
}
}
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
request.getSchema() 可以返回当前页面使用的协议,http 或是 https;
request.getServerName() 可以返回当前页面所在的服务器的名字;
request.getServerPort() 可以返回当前页面所在的服务器使用的端口
request.getContextPath() 可以返回当前页面所在的应用的名字
request.getRequestURL().toString(); 可以返回整个请求路径的名称