强制登录的功能,后端程序根据 Session
来判断用户是否登录,但是实现方法是比较麻烦的
有没有更简单的方法,同意拦截所有的请求,并进行 Session
校验呢,这里我们才有一种新的方法——拦截器
什么是拦截器?
拦截器是 Spring
框架提供的核心功能之一,主要用来拦截用户的请求,在指定方法前后,根据业务需要执行预先设定的代码
Session
中是否有登录用户的信息。如果有就可以放心,如果没有就进行拦截比如我们去银行办理业务,在办理业务前后,就可以加一些拦截操作
拦截器的使用步骤分为两步
实现 HandlerInterceptor
接口,并重写其所有方法
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("LoginInterceptor 目标方法执行前执行...");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("LoginInterceptor 目标方法执行后执行...");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("LoginInterceptor 视图渲染完毕后执行,最后执行");
}
}
preHandle()
方法:目标方法执行前执行 true
:继续执行后续操作false
:中断后续操作postHandle()
方法:目标方法执行后执行afterCompletion()
方法:视图渲染完毕后执行,最后执行(后端开发现在几乎不涉及视图,暂不了解)实现 webMvcConfigurer
接口,并重写 addInterceptors
方法
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册自定义拦截器对象
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**");
// 设置拦截器拦截的请求路径( /** 为拦截所有请求)
}
}
启动服务,试试访问任意请求,观察后端日志
preHandle
方法执行之后就放行了,开始执行目标方法,目标方法执行完成之后执行 postHandle
和 afterCompletion
方法我们把拦截器中 preHandle
方法的返回值改为 false
,再观察运行结果
拦截器的入门程序完成之后,接下来我们来介绍拦截器的使用细节。拦截器的使用细节我们主要介绍两个部分:
拦截路径是指我们定义的这个拦截器,对哪些请求生效
我们在注册配置拦截器的时候,通过 addPathPatterns()
方法指定要拦截哪些请求。也可以通过 excludePathPatterns()
指定不拦截哪些请求
/**
,表示拦截所有请求比如用户登录校验,我们希望可以对除了登录之外所有的路径生效
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
// 自定义拦截器对象
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册自定义拦截器对象
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("user/login");
// 设置拦截器拦截的请求路径( /** 为拦截所有请求)
}
}
在拦截器中除了设置 /**
拦截所有资源外,还有一些常见拦截路径设置:
拦截路径 | 含义 | 举例 |
---|---|---|
/* | 一级路径 | 能匹配 /user,/boook,/login,不能匹配 /user/login |
/** | 任意级路径 | 能匹配 /user,/user/login,/user/reg |
/book/* | /book 下的一级路径 | 能匹配 /book/addBook,不能匹配 /Book/addBook1,/book |
/book/** | /book 下的任意级路径 | 能匹配 /book,/book/addBook/,/book/addBook2,不能匹配 /user/login |
以上拦截器规则可以拦截项目中使用的
URL
,包括静态文件(图片文件,JS
和CSS
等文件)
正常调用顺序:
有了拦截器之后,会在调用 Controller
之前进行响应的业务处理,执行的流程为:
Controller
的方法之前,请求会被拦截器拦截。执行 preHandle()
方法,这个方法需要返回一个布尔类型的值
true
:就表示放行本次操作,继续访问 Controller
中的方法false
:就表示不会放行本次操作 Controller
中的方法也不会执行controller
中的方法执行完毕之后,再回过来执行 postHandle()
方法以及 afterCompletion()
方法,执行完毕之后,最终给浏览器响应数据
学习拦截器的基本操作之后,接下来我们需要完成最后一步操作:通过拦截器来完成图书管理系统中的登录校验功能
从 session
中获取用户信息,如果 session
中不存在,则返回 false
,并设置 HTTP
状态码为 401
,否则返回 true
import com.bite.book.constant.Constants;
import com.bite.book.model.UserInfo;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//true 表示放行 false表示拦截
log.info("LoginInterceptor preHandle....");
//获取session, 并且判断session中存储的userinfo信息是否为空
HttpSession session = request.getSession();
UserInfo userInfo = (UserInfo) session.getAttribute(Constants.USER_SESSION_KEY);
if (userInfo==null || userInfo.getId()<=0){
//用户未登录
response.setStatus(401);
return false;
}
return true;
}
}
http
状态码401
:Unauthorized
401
状态码表示不接受这些凭证import com.bite.book.interceptor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/user/login")
.excludePathPatterns("/css/**")
.excludePathPatterns("/js/**")
.excludePathPatterns("/pic/**")
.excludePathPatterns("/**/*.html")
;
}
}
删除之前的登录校验代码:
/**
* 查询图书列表
*/
@RequestMapping("/getBookListByPage")
public Result<PageResult<BookInfo>> getBookListByPage(PageRequest pageRequest, HttpSession session){
log.info("查询图书列表, 请求参数pageRequest: {}", pageRequest);
// //从session获取用户信息
// //如果用户信息为空, 说明用户未登录
// UserInfo loginUserInfo = (UserInfo) session.getAttribute(Constants.USER_SESSION_KEY);
// if (loginUserInfo==null || loginUserInfo.getId()<=0){
// return Result.nologin();
// }
//参数校验, 自己补充
PageResult<BookInfo> bookList = bookService.getBookListByPage(pageRequest);
return Result.success(bookList);
}