Interceptor拦截器
咳咳,这个东东还是蛮好的哦,在项目里面哈,有很多的子模块需要做相同的请求处理,如果每个子模块都去写的话,那就太麻烦了,而且修改维护起来也不方便,耦合性也高,所以就需要用到我们今天学习的Interceptor拦截器啦,他可以很好的解耦合。
现在我们来写一个拦截器吧
@Component
public class AlphaInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(AlphaInterceptor.class);
// 在controller之前执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.debug("preHandle: " + handler.toString());
return true;
}
// 调用完controller之后执行的
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.debug("postHandle: " + handler.toString());
}
// 在templateEngine之后执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.debug("afterCompletion: " + handler.toString());
}
}
- 咳咳,为了待会方便看效果,这里我们搞一个日志文件,以便于待会的观看哈
- 拦截器里面大多用到 preHandle 、 postHandle 、 afterCompletion;
- 分别表示,controller之前拦截,之后拦截,最后拦截
- 记得加上个@Component ,把拦截器交给Spring容器哦
拦截器写好了,我们还要给他进行注册,在WebMvc里面
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private AlphaInterceptor alphaInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(alphaInterceptor)
.excludePathPatterns("/**/*.css","/**/*.js","/**/*.png","/**/*.jpg","/**/*.jpeg") // 不拦截静态资源访问
.addPathPatterns("/register","/login"); // 添加的拦截路径
}
}
- 搞完拉,这是一个配置类哈,也是要加注解的
- add方法里面,可以添加该拦截器的拦截路径和不拦截的路径,不写的话就是全部拦截哦
然后我们来看看效果!!!
- 这里我们可以很明显的看出他们的执行顺序哈。
- 然后了解运作过程后,我们就来实战一下吧!
项目实战
实战背景: 我们的导航栏里面有消息、登录、注册、个人中心,然后我们处于登录状态的时候,要在html模板上面隐藏掉 登录和注册,相反就影藏掉 消息和 个人中心
解决方法
- 我们就配一个拦截器,除了静态资源的访问,其他的全部拦截,然后通过Cookie里面的ticket登录凭证来获取用户
- 之后将将用户存放到model里面去。
- 在html模板上面判断,loginUser是否存在,然后根据判断进行分别影藏
先创建拦截器
@Component
public class LoginTicketInterceptor implements HandlerInterceptor {
@Autowired
private UserService userService;
@Autowired
private HostHolder hostHolder;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String ticket = CookieUtil.getValue(request, "ticket");
if(ticket != null){
// 查询凭证
LoginTicket loginTicket = userService.findLoginTicket(ticket);
// 检查凭证是否有效 不为空、状态表示在线、超时时间晚于当前时间
if(loginTicket != null && loginTicket.getStatus() == 0 && loginTicket.getExpired().after(new Date())){
User user = userService.findUserById(loginTicket.getUserId()); // 根据凭证查询用户
hostHolder.setUser(user);
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
User user = hostHolder.getUser();
if(user != null && modelAndView != null){
modelAndView.addObject("loginUser",user);
}
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
hostHolder.clear();
}
}
- 首先哈,ticket登录凭证是存放在Cookie里面的。
- 还好的是我们的request参数里面是可以取到Cookie的。
- 然后顾及到其他地方也会使用到在request里面根据String来取Cookie值的操作。
- 所以我们将它抽取出来,成一个工具类。
public class CookieUtil {
public static String getValue(HttpServletRequest request,String name){
if(request == null || name == null){
throw new IllegalArgumentException("参数为空");
}
Cookie[] cookies = request.getCookies();
if(cookies != null){
for (Cookie cookie : cookies) {
if(cookie.getName().equals(name)){
return cookie.getValue();
}
}
}
return null;
}
}
- 注意哈,这里我们直接使用的是static方法,所以就不交给Spring容器管理了,直接使用类.方法的形式来调用哈。
- 然后方法也很简单,给request请求和要查询的cookie名称。
- 先通过request获取所有的cookie,然后遍历,再返回值即可。
当我们获取到凭证后,我们就需要进行判断了,如果不存在,就表示没有登录,我们啥也不用管了,如果存在,我们就要进行细节处理了
- 我们根据这个ticket值,来查询登录凭证是否是有效的,
- 即:检查该登录凭证是否为空 、 状态是否是表示在线 、 超时的时间是否是晚于当前时间
loginTicket != null
loginTicket.getStatus() == 0
loginTicket.getExpired().after(new Date())
- 如果都通过了,我们就可以获取他的userId;然后再获取一个User对象
- 然后我们需要把这个user对象存起来,这里要考虑到多线程并发的环境哈,
- 所以我们搞一个多线程的工具类,用于存对象
// 执有用户的信息,代替session对象
@Component
public class HostHolder {
private ThreadLocal<User> users = new ThreadLocal<>();
public void setUser(User user) {
users.set(user);
}
public User getUser(){
return users.get();
}
public void clear(){
users.remove();
}
}
- 本来是可以存session里面的,但是因为是多线程,如果出错,而且还给服务器添加负担,所以还是多线程工具类比较香。
- 在pre方法运行完之后,就是controller层处理,处理完就是要返回给html模板,然后这个时候就会给我们的post方法给拦截下来,
- 拦截后,我们就将这个user对象,放入model里面去,这样到html模板的时候,model里就会有user对象的存在了。
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
User user = hostHolder.getUser();
if(user != null && modelAndView != null){
modelAndView.addObject("loginUser",user);
}
}
- 当然啦,我们还需要在最后的拦截里面,进行clear清理操作哈!!!
- 这样,基本就搞定啦!!!
- 这样就搞定啦,因为每个页面都要这样显示,所有我们要搞全部拦截哈!
- 溜拉溜拉。