Spring Boot-Filter過濾器使用

零、本文綱要

  • 一、Filter作用
  • 二、Filter使用
    1、基礎(chǔ)準(zhǔn)備
    2、編寫Filter
    3、掃描Filter
    4、測(cè)試
  • 三、使用總結(jié)
  • 補(bǔ)充:完整Filter實(shí)現(xiàn)類代碼

一、Filter作用

  • ① 權(quán)限控制;

  • ② 對(duì)request、response攔截處理;

  • ③ 公共代碼提取。

二、Filter使用

1、基礎(chǔ)準(zhǔn)備

  • ① 引入依賴
<!--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
  • ③ 編寫啟動(dòng)類
@Slf4j // lombok 提供的日志注解,方便直接使用 log 輸出指定日志
@SpringBootApplication
public class TemplateApplication {
    public static void main(String[] args) {
        SpringApplication.run(ReggieApplication.class, args);
        log.info("項(xiàng)目啟動(dòng)成功!");
    }
}

2、編寫Filter

  • 基礎(chǔ):Filter接口
public interface Filter {
    default void init(FilterConfig filterConfig) throws ServletException {
    }

    void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException; // 此處關(guān)注此方法

    default void destroy() {
    }
}

此處我們重點(diǎn)關(guān)注doFilter方法,編寫實(shí)現(xiàn)類重寫該方法。

  • ① 編寫登錄過濾器

Ⅰ 在自定義Filter實(shí)現(xiàn)類上使用@WebFilter注解;

Ⅱ 自定義filterName(隨意,不要與其他Filter重復(fù)即可);

Ⅲ 定義urlPatterns此處設(shè)置為"/*",表示攔截所有請(qǐng)求;

/**
 * 檢查用戶是否已經(jīng)登錄的過濾器
 */
@Slf4j
@WebFilter(filterName = "loginCheckFilter", urlPatterns = "/*")
public class LoginCheckFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
            ... ...
    }
}
  • ② 編寫詳細(xì)方法體

Ⅰ 將傳入的requestresponse對(duì)象轉(zhuǎn)換為Http類型;

HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;

Ⅱ 自定義不需要處理的URI數(shù)組

// 定義不需要處理的請(qǐng)求路徑
String[] urls = new String[]{
        "/employee/login", // 員工登錄
        "/employee/logout", // 員工登出
        "/backend/**", // 管理后臺(tái)靜態(tài)資源
        "/front/**", // 移動(dòng)端靜態(tài)資源
        "/common/**", // 通用部分
        "/user/sendMsg", // 移動(dòng)端短信驗(yàn)證碼
        "/user/login" // 移動(dòng)端登錄
};

Ⅲ 獲取請(qǐng)求URI

// A. 獲取本次請(qǐng)求的URI
String requestURI = request.getRequestURI();

Ⅳ 判斷本次請(qǐng)求

// B. 判斷本次請(qǐng)求, 是否需要登錄, 才可以訪問
boolean check = checkURI(urls, requestURI);

封裝checkURI方法,如下:

a、注入PATH_MATCHER,用于路徑比較,如下:

// Spring 框架提供的用于路徑比較的類:路徑匹配器,支持通配符
public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

b、方法體,如下:

/**
 * 路徑匹配,檢查本次請(qǐng)求是否需要放行
 * @param urls 需放行的 urls
 * @param requestURI 請(qǐng)求進(jìn)來的 URI
 * @return 放回結(jié)果
 */
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("本次{}請(qǐng)求不需要處理!", requestURI);
    filterChain.doFilter(request, response);
    return;
}

Ⅵ 其余路徑,判斷登錄狀態(tài)

a、已登錄,則放行

BaseContext是自定義存儲(chǔ)線程userId的類

// D. 判斷登錄狀態(tài),如果已登錄,則直接放行
// 【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向前端返回響應(yīng)數(shù)據(jù)R.error(NOT_LOGIN)

// E. 如果未登錄, 則返回未登錄結(jié)果
// 通過輸出流方式,向客戶端頁(yè)面響應(yīng)數(shù)據(jù)
log.info("用戶未登錄!");
response.getWriter().write(JSON.toJSONString(R.error(NOT_LOGIN)));

3、掃描Filter

在啟動(dòng)類上添加@ServletComponentScan注解,用于掃描 @WebFilter 注解,如下:

@Slf4j // lombok 提供的日志注解,方便直接使用 log 輸出指定日志
@SpringBootApplication
@ServletComponentScan // 掃描 @WebFilter 注解
public class ReggieApplication {
    public static void main(String[] args) {
        SpringApplication.run(ReggieApplication.class, args);
        log.info("項(xiàng)目啟動(dòng)成功!");
    }
}

4、測(cè)試

編寫Controller類,啟動(dòng)測(cè)試。

三、使用總結(jié)

  • ① 攔截所有請(qǐng)求;
  • ② 自定義放行URI數(shù)組;
  • ③ 對(duì)比放行URI數(shù)組直接放行;
  • ④ 未直接放行部分判斷條件,滿足放行;
  • ⑤ 不滿足,使用response對(duì)象返回結(jié)果響應(yīng)。

補(bǔ)充:完整Filter實(shí)現(xiàn)類代碼

/**
 * 檢查用戶是否已經(jīng)登錄的過濾器
 */
@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. 獲取本次請(qǐng)求的URI
        String requestURI = request.getRequestURI();
        log.info("攔截到請(qǐng)求:{}", requestURI);
        // 定義不需要處理的請(qǐng)求路徑
        String[] urls = new String[]{
                "/employee/login",
                "/employee/logout",
                "/backend/**",
                "/front/**",
                "/common/**",
                "/user/sendMsg", // 移動(dòng)端短信驗(yàn)證碼
                "/user/login" // 移動(dòng)端登錄
        };

        // B. 判斷本次請(qǐng)求, 是否需要登錄, 才可以訪問
        boolean check = checkURI(urls, requestURI);

        // C. 如果不需要攔截,則直接放行
        if (check){
            log.info("本次{}請(qǐng)求不需要處理!", requestURI);
            filterChain.doFilter(request, response);
            return;
        }

        // D. 判斷登錄狀態(tài),如果已登錄,則直接放行
        // 【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. 如果未登錄, 則返回未登錄結(jié)果
        // 通過輸出流方式,向客戶端頁(yè)面響應(yīng)數(shù)據(jù)
        log.info("用戶未登錄!");
        response.getWriter().write(JSON.toJSONString(R.error(NOT_LOGIN)));
    }

    /**
     * 路徑匹配,檢查本次請(qǐng)求是否需要放行
     * @param urls 需放行的 urls
     * @param requestURI 請(qǐng)求進(jìn)來的 URI
     * @return 放回結(jié)果
     */
    public boolean checkURI(String[] urls, String requestURI){
        for (String url : urls) {
            boolean match = PATH_MATCHER.match(url, requestURI);
            if (match) return true;
        }
        return false;
    }
}

四、結(jié)尾

以上即為Filter基礎(chǔ)使用的內(nèi)容,感謝閱讀。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容