零、本文纲要
- 一、Filter作用
- 二、Filter使用
1、基础准备
2、编写Filter
3、扫描Filter
4、测试 - 三、使用总结
- 补充:完整Filter实现类代码
一、Filter作用
① 权限控制;
② 对request、response拦截处理;
③ 公共代码提取。
二、Filter使用
1、基础准备
- ① 引入依赖
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-web
compile
- ② 编写配置文件
server:
port: 8080
- ③ 编写启动类
@Slf4j // lombok 提供的日志注解,方便直接使用 log 输出指定日志
@SpringBootApplication
public class TemplateApplication {
public static void main(String[] args) {
SpringApplication.run(ReggieApplication.class, args);
log.info("项目启动成功!");
}
}
2、编写Filter
- 基础:Filter接口
public interface Filter {
default void init(FilterConfig filterConfig) throws ServletException {
}
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException; // 此处关注此方法
default void destroy() {
}
}
此处我们重点关注doFilter方法
,编写实现类重写该方法。
- ① 编写登录过滤器
Ⅰ 在自定义Filter实现类上使用@WebFilter注解;
Ⅱ 自定义filterName(随意,不要与其他Filter重复即可);
Ⅲ 定义urlPatterns此处设置为"/*",表示拦截所有请求;
/**
* 检查用户是否已经登录的过滤器
*/
@Slf4j
@WebFilter(filterName = "loginCheckFilter", urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
... ...
}
}
- ② 编写详细方法体
Ⅰ 将传入的request
与response
对象转换为Http类型
;
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
Ⅱ 自定义不需要处理的URI数组
// 定义不需要处理的请求路径
String[] urls = new String[]{
"/employee/login", // 员工登录
"/employee/logout", // 员工登出
"/backend/**", // 管理后台静态资源
"/front/**", // 移动端静态资源
"/common/**", // 通用部分
"/user/sendMsg", // 移动端短信验证码
"/user/login" // 移动端登录
};
Ⅲ 获取请求URI
// A. 获取本次请求的URI
String requestURI = request.getRequestURI();
Ⅳ 判断本次请求
// B. 判断本次请求, 是否需要登录, 才可以访问
boolean check = checkURI(urls, requestURI);
封装checkURI方法,如下:
a、注入PATH_MATCHER,用于路径比较,如下:
// Spring 框架提供的用于路径比较的类:路径匹配器,支持通配符
public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
b、方法体,如下:
/**
* 路径匹配,检查本次请求是否需要放行
* @param urls 需放行的 urls
* @param requestURI 请求进来的 URI
* @return 放回结果
*/
public boolean checkURI(String[] urls, String requestURI){
for (String url : urls) {
boolean match = PATH_MATCHER.match(url, requestURI);
if (match) return true;
}
return false;
}
Ⅴ 不需要拦截,则直接放行
// C. 如果不需要拦截,则直接放行
if (check){
log.info("本次{}请求不需要处理!", requestURI);
filterChain.doFilter(request, response);
return;
}
Ⅵ 其余路径,判断登录状态
a、已登录,则放行
BaseContext是自定义存储线程userId的类
// D. 判断登录状态,如果已登录,则直接放行
// 【EMPLOYEE】:用于员工登录判断
Long empId = (Long) request.getSession().getAttribute(CUR_EMPLOYEE);
if (empId != null){
log.info("用户已登录,用户ID为:{}", empId);
// 将 empId 存入 ThreadLocal
BaseContext.setCurrentId(empId);
filterChain.doFilter(request, response);
return;
}
// 【USER】:用于用户登录判断
Long userId = (Long) request.getSession().getAttribute(CUR_USER);
if (userId != null){
log.info("用户已登录,用户ID为:{}", userId);
// 将 userId 存入 ThreadLocal
BaseContext.setCurrentId(userId);
filterChain.doFilter(request, response);
return;
}
b、未登录,拦截
自定义常量
public static final String NOT_LOGIN = "NOTLOGIN"; // LoginCheckFilter 拦截器使用
拦截,此处使用response向前端返回响应数据R.error(NOT_LOGIN)
// E. 如果未登录, 则返回未登录结果
// 通过输出流方式,向客户端页面响应数据
log.info("用户未登录!");
response.getWriter().write(JSON.toJSONString(R.error(NOT_LOGIN)));
3、扫描Filter
在启动类上添加@ServletComponentScan注解
,用于扫描 @WebFilter 注解
,如下:
@Slf4j // lombok 提供的日志注解,方便直接使用 log 输出指定日志
@SpringBootApplication
@ServletComponentScan // 扫描 @WebFilter 注解
public class ReggieApplication {
public static void main(String[] args) {
SpringApplication.run(ReggieApplication.class, args);
log.info("项目启动成功!");
}
}
4、测试
编写Controller类,启动测试。
三、使用总结
- ① 拦截所有请求;
- ② 自定义放行URI数组;
- ③ 对比放行URI数组直接放行;
- ④ 未直接放行部分判断条件,满足放行;
- ⑤ 不满足,使用response对象返回结果响应。
补充:完整Filter实现类代码
/**
* 检查用户是否已经登录的过滤器
*/
@Slf4j
@WebFilter(filterName = "loginCheckFilter", urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
// Spring 框架提供的用于路径比较的类:路径匹配器,支持通配符
public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// A. 获取本次请求的URI
String requestURI = request.getRequestURI();
log.info("拦截到请求:{}", requestURI);
// 定义不需要处理的请求路径
String[] urls = new String[]{
"/employee/login",
"/employee/logout",
"/backend/**",
"/front/**",
"/common/**",
"/user/sendMsg", // 移动端短信验证码
"/user/login" // 移动端登录
};
// B. 判断本次请求, 是否需要登录, 才可以访问
boolean check = checkURI(urls, requestURI);
// C. 如果不需要拦截,则直接放行
if (check){
log.info("本次{}请求不需要处理!", requestURI);
filterChain.doFilter(request, response);
return;
}
// D. 判断登录状态,如果已登录,则直接放行
// 【EMPLOYEE】:用于员工登录判断
Long empId = (Long) request.getSession().getAttribute(CUR_EMPLOYEE);
if (empId != null){
log.info("用户已登录,用户ID为:{}", empId);
// 将 empId 存入 ThreadLocal
BaseContext.setCurrentId(empId);
filterChain.doFilter(request, response);
return;
}
// 【USER】:用于用户登录判断
Long userId = (Long) request.getSession().getAttribute(CUR_USER);
if (userId != null){
log.info("用户已登录,用户ID为:{}", userId);
// 将 empId 存入 ThreadLocal
BaseContext.setCurrentId(userId);
filterChain.doFilter(request, response);
return;
}
// E. 如果未登录, 则返回未登录结果
// 通过输出流方式,向客户端页面响应数据
log.info("用户未登录!");
response.getWriter().write(JSON.toJSONString(R.error(NOT_LOGIN)));
}
/**
* 路径匹配,检查本次请求是否需要放行
* @param urls 需放行的 urls
* @param requestURI 请求进来的 URI
* @return 放回结果
*/
public boolean checkURI(String[] urls, String requestURI){
for (String url : urls) {
boolean match = PATH_MATCHER.match(url, requestURI);
if (match) return true;
}
return false;
}
}
四、结尾
以上即为Filter基础使用的内容,感谢阅读。