Spring Boot-Filter过滤器使用

零、本文纲要

  • 一、Filter作用
  • 二、Filter使用
    1、基础准备
    2、编写Filter
    3、扫描Filter
    4、测试
  • 三、使用总结
  • 补充:完整Filter实现类代码

一、Filter作用

  • ① 权限控制;

  • ② 对request、response拦截处理;

  • ③ 公共代码提取。

二、Filter使用

1、基础准备

  • ① 引入依赖
<!--spring_boot-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<!--test-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<!--web-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <scope>compile</scope>
</dependency>
  • ② 编写配置文件
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 {
            ... ...
    }
}
  • ② 编写详细方法体

Ⅰ 将传入的requestresponse对象转换为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基础使用的内容,感谢阅读。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,752评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,100评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,244评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,099评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,210评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,307评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,346评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,133评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,546评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,849评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,019评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,702评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,331评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,030评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,260评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,871评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,898评论 2 351

推荐阅读更多精彩内容