Spring Boot由攔截器HandlerInterceptor實現(xiàn)登錄認真、API耗時統(tǒng)計和資源攔截
??背景: 在項目中我使用了自定義的Filter,它過濾了很多路徑,當然對靜態(tài)資源是直接放過去的,但是,還是出現(xiàn)了靜態(tài)資源沒辦法訪問到Spring Boot默認的文件夾中的文件。另外,經(jīng)常需要判斷當前訪問者是否有權操作某個資源。
??博文更新時間:2022-02。
靜態(tài)資源攔截
??說下默認映射的文件夾有:
- classpath:/META-INF/resources
- classpath:/resources
- classpath:/static
- classpath:/public
??上面這幾個都是靜態(tài)資源的映射路徑,優(yōu)先級從上向下依次降低。我們可以通過修改spring.mvc.static-path-pattern來修改默認的映射**
***接管Spring Boot 2.5.0 的Web配置 ********* 這是重點中的重點
??如果Spring Boot提供的Sping MVC不符合要求,則可以通過一個配置類(注解有@Configuration的類)加上@EnableWebMvc注解來實現(xiàn)完全自己控制的MVC配置。
??當然,通常情況下,Spring Boot的自動配置是符合我們大多數(shù)需求的。在你既需要保留Spring Boot提供的便利,又需要增加額外配置的時候,可以定義一個配置類并繼承WebMvcConfigurerAdapter,而無需使用@EnableWebMvc注解。這里我們提到WebMvcConfigurerAdapter類,重寫這個類中的方法可以讓我們增加額外的配置。
自定義資源映射addResourceHandlers
??比如,我們想自定義靜態(tài)資源映射目錄的話,只需重寫addResourceHandlers方法即可。通過addResourceHandler添加映射路徑,然后通過addResourceLocations來指定路徑。我們訪問自定義my文件夾中
@Configuration
public class MyWebMvcConfigurerAdapter extends WebMvcConfigurerAdapter {
/**
* 配置靜態(tài)訪問資源
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/my/**").addResourceLocations("classpath:/my/");
super.addResourceHandlers(registry);
}
}
??如果你想指定外部的目錄也很簡單,直接用addResourceLocations指定即可,代碼如下:
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/my/**").addResourceLocations("file:E:/my/");
super.addResourceHandlers(registry);
}
addResourceLocations指的是文件放置的目錄,addResoureHandler指的是對外暴露的訪問路徑。
HandlerInterceptor方法介紹
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception;
void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception;
??方法解釋:
preHandle:在業(yè)務處理器處理請求之前被調用。預處理,可以進行編碼、安全控制、權限校驗等處理。
postHandle:在業(yè)務處理器處理請求執(zhí)行完成后,生成視圖之前執(zhí)行。它調用Service并返回ModelAndView,但未進行頁面渲染。在此處我們有機會修改ModelAndView,但是博主不推薦這么操作。
afterCompletion:在DispatcherServlet處理完請求后被調用,可用于清理資源等。返回已經(jīng)渲染的頁面。
添加攔截器
??攔截器在我們項目中經(jīng)常使用,這里就來介紹兩個最簡單的使用場景:①根據(jù)session判斷用戶是否登錄,② API執(zhí)行耗時。要實現(xiàn)攔截器功能需要完成以下2個步驟:
- 創(chuàng)建攔截器類并實現(xiàn) HandlerInterceptor 接口
- 重寫WebMvcConfigurationSupport中的addInterceptors方法添加自定義攔截器
??首先,自定義攔截器:
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.Duration;
import java.time.Instant;
/**
* 攔截器
*/
@Slf4j
@Component
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
request.setAttribute("starTime", Instant.now());
boolean flag =true;
// 判斷用戶是否登錄
User user=(User)request.getSession().getAttribute("user");
if(null==user){
response.sendRedirect("toLogin");
flag = false;
}
return flag;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("方法 {} 耗時:--> {}", ((HandlerMethod) handler).getMethod().getName(),
Duration.between((Instant) request.getAttribute("starTime"), Instant.now()).toMillis());
}
}
??此段代碼簡單實現(xiàn)了兩個功能:① 根據(jù)session中是否有User對象來判斷是否登錄,為空就跳轉到登錄頁,不為空就通過;② 統(tǒng)計API執(zhí)行所耗費的時間。接著,重寫WebMvcConfigurerAdapter中的攔截器注冊方法addInterceptors:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import javax.annotation.Resource;
@Configuration
public class ApiConfig extends WebMvcConfigurationSupport {
@Resource
private ControllerTimeInterceptor controllerTimeInterceptor;
@Override
protected void addInterceptors(InterceptorRegistry registry) {
// 添加攔截器
registry.addInterceptor(controllerTimeInterceptor).addPathPatterns("/**")
.excludePathPatterns("/admin/login", "/admin/getLogin");
super.addInterceptors(registry);
}
}
??addPathPatterns("/**")表示攔截所有請求,但是 excludePathPatterns 排除了/toLogin和/login請求的攔截。
- addInterceptors:添加自定義攔截器的實現(xiàn)邏輯類。
- addPathPatterns:添加待攔截的請求路徑,可以添加多個路徑。路徑配置規(guī)則:
/**匹配所有路徑,/**/login/**匹配包含 /login/ 關鍵路徑的所有路徑。 - excludePathPatterns:添加不需要攔截的請求路徑。
??攔截效果如下:
2022-02-19 12:36:38.898 INFO 37941 --- [nio-8087-exec-1] c.s.d.config.ControllerTimeInterceptor : 方法 readConfig 耗時:--> 88
擴展思考
Q:除了使用session認證方式實現(xiàn)登錄外,還有哪些做法可以實現(xiàn)自動認證登錄?
A:web開發(fā)中還常用cookie來做認證登錄。也可以使用Spring AOP攔截。
Reference
Buy me a coffee. ?Get red packets.
浙公網(wǎng)安備 33010602011771號