Spring Boot 使用攔截器
1. 前言
攔截器這個名詞定義的非常形象,就像導彈要攻擊目標的時候,可能會被先進的反導系統(tǒng)攔截,此處的反導系統(tǒng)就是一種攔截器。
我們開發(fā)的應(yīng)用,對外暴露的是控制器中定義的 API 方法,我們可以在 API 方法的外圍放置攔截器,所有對 API 的訪問都可以通過攔截器進行過濾。
OK,那么這樣的攔截有什么意義嗎,其實已經(jīng)很明顯了,反導系統(tǒng)可以保護目標的安全并識別對目標的攻擊行為。同理,攔截器可以跟蹤對應(yīng)用的訪問行為,對合法訪問行為予以放行,對非法訪問行為予以拒絕。怎么樣,是不是很牛,接下來咱們就在 Spring Boot 項目中具體實現(xiàn)下。
2. 跟蹤訪問行為
要想實現(xiàn)對訪問的攔截,首先要能跟蹤訪問行為,我們在 Spring Boot 中引入攔截器來實現(xiàn)下。
2.1 使用 Spring Initializr 創(chuàng)建項目
Spring Boot 版本選擇 2.2.5 ,Group 為 com.5axxw , Artifact 為 spring-boot-interceptor ,生成項目后導入 Eclipse 開發(fā)環(huán)境。
2.2 引入項目依賴
引入 Web 項目依賴即可。
實例:
<!-- web項目依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2.3 創(chuàng)建攔截器
創(chuàng)建的類實現(xiàn) HandlerInterceptor 接口,即可成為攔截器類.
實例:
/**
* 自定義攔截器類
*/
public class MyInterceptor implements HandlerInterceptor {// 實現(xiàn)HandlerInterceptor接口
/**
* 訪問控制器方法前執(zhí)行
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println(new Date() + "--preHandle:" + request.getRequestURL());
return true;
}
/**
* 訪問控制器方法后執(zhí)行
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println(new Date() + "--postHandle:" + request.getRequestURL());
}
/**
* postHandle方法執(zhí)行完成后執(zhí)行,一般用于釋放資源
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println(new Date() + "--afterCompletion:" + request.getRequestURL());
}
}
在上面的實例中,我們定義了一個攔截器類 MyInterceptor ,通過實現(xiàn) HandlerInterceptor 接口,該類具備了攔截器的功能。
MyInterceptor 中的方法執(zhí)行順序為 preHandle – Controller 方法 – postHandle – afterCompletion ,所以攔截器實際上可以對 Controller 方法執(zhí)行前后進行攔截監(jiān)控。
最后還有一個非常重要的注意點, preHandle 需要返回布爾類型的值。 preHandle 返回 true 時,對控制器方法的請求才能到達控制器,繼而到達 postHandle 和 afterCompletion 方法;如果 preHandle 返回 false ,后面的方法都不會執(zhí)行。
2.4 配置攔截器
上一步我們開發(fā)了配置器類,如果想讓配置器生效,還需要通過配置類進行相應(yīng)配置。
實例:
/**
* Web配置類
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
/**
* 添加Web項目的攔截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 對所有訪問路徑,都通過MyInterceptor類型的攔截器進行攔截
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
}
}
2.5 創(chuàng)建控制器
我們建立一個簡單的控制器,實現(xiàn)登錄方法,以便檢驗攔截器的效果。
實例:
/**
* 登錄控制器
*/
@RestController
public class LoginController {
@RequestMapping("/login")
public boolean login(String username, String password) {
System.out.println(new Date() + " 某用戶嘗試登錄,用戶名:" + username + " 密碼:" + password);
return true;
}
}
2.6 跟蹤訪問行為
運行啟動類,訪問 http://127.0.0.1:8080/login?username=5axxw&password=123
,控制臺輸出如下:
可見我們已經(jīng)完整的跟蹤了一次對 http://127.0.0.1:8080/login
接口的訪問。
3. 實現(xiàn)訪問控制
區(qū)分合法、非法訪問,最常見的就是根據(jù)用戶的登錄狀態(tài)、角色判斷。接下來我們就演示下,對未登錄用戶非法訪問請求的攔截。
3.1 修改控制器方法
修改登錄方法,當用戶輸入的用戶名和密碼正確時,通過 Session 記錄登錄人信息。
然后開發(fā)獲取登錄人員信息方法,返回 Session 中記錄的登錄人信息。
實例:
/**
* 登錄控制器
*/
@RestController
public class LoginController {
/**
* 登錄方法
*/
@RequestMapping("/login")
public boolean login(HttpServletRequest request, String username, String password) {
if ("5axxw".equals(username) && "123".equals(password)) {
// 登錄成功,則添加Session并存儲登錄用戶名
request.getSession().setAttribute("LOGIN_NAME", username);
return true;
}
return false;
}
/**
* 獲取登錄人員信息
*/
@RequestMapping("/info")
public String info(HttpServletRequest request) {
return "您就是傳說中的:" + request.getSession().getAttribute("LOGIN_NAME");
}
}
3.2 修改攔截器方法
由于用戶在登錄之前還沒有設(shè)置 Session ,所以登錄方法不應(yīng)該攔截,可以讓用戶自由請求。但是只有登錄成功后的用戶,也就是說具備 Session 的用戶才能訪問 info 方法。
實例:
/**
* 自定義攔截器類
*/
public class MyInterceptor implements HandlerInterceptor {// 實現(xiàn)HandlerInterceptor接口
/**
* 訪問控制器方法前執(zhí)行
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if (request.getRequestURI().contains("/login") == true) {// 登錄方法直接放行
return true;
} else {// 其他方法需要先檢驗是否存在Session
if (request.getSession().getAttribute("LOGIN_NAME") == null) {//未登錄的不允許訪問
return false;
} else {
return true;
}
}
}
}
3.3 測試
首先直接請求 http://127.0.0.1:8080/info
,由于此時未登錄,所以請求被攔截,網(wǎng)頁輸出如下:
如果先請求登錄方法 http://127.0.0.1:8080/login?username=5axxw&password=123
,然后訪問 http://127.0.0.1:8080/info
,則網(wǎng)頁輸出:
4. 小結(jié)
Spring Boot 的攔截器能夠管理對控制器方法的訪問請求,通過使用攔截器,可以實現(xiàn)訪問控制,加強項目的安全性。
當然對于更加復雜的安全管理續(xù)期, Spring Boot 也可以快速的整合 Spring Security 或 Shiro ,以構(gòu)建企業(yè)級的安全管理體系,在后續(xù)章節(jié)再進一步介紹吧。