HandlerInterceptor基于JDK动态代理实现request的拦截。接口共有3个方法:
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;
}
1.preHandle,在访问真正的request之前的预处理,只有该方法返回true时候,请求才能到达controller。因此我们可以在此对request做一定处理。比如判断用户是否有权限访问该接口、对接口访问做日志记录等。
2.postHandle,只有当preHandle返回true之后,才能执行该接口,该接口是在请求完成之后,DispatcherServlet进行视图返回渲染之前调用。一般在此接口我们可以操作ModelAndView,在实际当中我们可以在此处设置静态文件的版本号,jsp中的静态文件引用只需要引用改变量,就可以实现动态刷新静态文件。
3.afterCompletion,该接口也是只有当preHandle返回true之后才会执行,它是在DispatcherServlet渲染完模板之后才会调用。一般在次方法中清除缓存变量等。比如我们想实现一个用户请求过来,验证完权限之后,我们在preHandle中将用户信息放到ThreadLocal中,后面的controller,service就直接可以从该接ThreadLocal中获取用户信息。但当该请求完成之后,需要在afterCompletion中清理该ThreadLocal变量。
下面示例主要是实现用户权限的拦截,比较简单,该示例中用到了自定义注解,对于controller中加了注解的接口,在拦截器可以根据注解设置不同的拦截;如下面设置了Permition注解 124100740.png,表示该接口开放。
拦截器
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Value("${session.timeout}")
private long EXPIRE_TIME;//用户session缓存时间
@Autowired
private RedisOperations redisOperations;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler == null || handler instanceof ResourceHttpRequestHandler) {
return true;
}
if(handler instanceof HandlerMethod){
String name= ((HandlerMethod) handler).getMethod().getName();
if(("errorHtml".equalsIgnoreCase(name))){
throw new PageException("the url: "+request.getRequestURL()+" not exists.");
}
}
return verify(request,handler);
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
UserThreadLocal.clear();
}
private boolean verify(HttpServletRequest request, Object handler){
Permition permition = verifyPermit(handler);
if(permition!=null && permition.oriented()==Permit.All){
return true;
}
return verifyUser(request);
}
private boolean verifyUser(HttpServletRequest request){
//假设用户的身份信息是从header上传,从header中取出身份信息
String headerKey= request.getHeader(UserKeyUtils.IDENTITY);
if(StringUtils.isEmpty(headerKey)){
throw new BusinessException(0,"no authority.");
}
//从身份信息中提取出用户key
String userRedisKey= UserKeyUtils.getUserRedisKey(headerKey);
//从缓存中获取用户信息
User user= redisOperations.getVal(userRedisKey,User.class);
if(user==null){
throw new BusinessException(-1,"token invalidate or expired.");
}
if(StringUtils.isEmpty(user.schoolId)){
throw new BusinessException("schoolId not empty.");
}
user.token = headerKey;
//验证完成之后会将用户信息存入redis
redisOperations.putVal(userRedisKey,user,EXPIRE_TIME);
//用户信息存入ThreadLocal
UserThreadLocal.set(user);
return true;
}
//权限验证,可以在此处扩展更复杂的权限控制,比如基于url的资源拦截。
private Permition verifyPermit(Object handler) {
Permition permit= null;
if (handler instanceof HandlerMethod){
permit = ((HandlerMethod) handler).getMethod().getAnnotation(Permition.class);
if(permit == null){
permit = ((HandlerMethod) handler).getBeanType().getAnnotation(Permition.class);
}
}
return permit;
}
}
UserThreadLocal
public class UserThreadLocal {
private static final ThreadLocal<User> userThreadLocal= new ThreadLocal<>();
public static User get(){
return userThreadLocal.get();
}
public static void set(User user){
userThreadLocal.set(user);
}
public static void clear(){
userThreadLocal.remove();
}
}
网友评论