零、本文綱要
- 一、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ì)方法體
Ⅰ 將傳入的request與response對(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)容,感謝閱讀。