SpringBoot之HandlerInterceptor攔截器的使用詳解
前言
平常項(xiàng)目開發(fā)過程中,會遇到登錄攔截,權(quán)限校驗(yàn),參數(shù)處理,防重復(fù)提交等問題,那攔截器就能幫我們統(tǒng)一處理這些問題。
一、實(shí)現(xiàn)方式
1.1 自定義攔截器
自定義攔截器,即攔截器的實(shí)現(xiàn)類,一般有兩種自定義方式:
定義一個類,實(shí)現(xiàn)org.springframework.web.servlet.HandlerInterceptor接口。
定義一個類,繼承已實(shí)現(xiàn)了HandlerInterceptor接口的類,例如org.springframework.web.servlet.handler.HandlerInterceptorAdapter抽象類。
1.2 添加Interceptor攔截器到WebMvcConfigurer配置器中
自定義配置器,然后實(shí)現(xiàn)WebMvcConfigurer配置器。
以前一般繼承org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter類,不過SrpingBoot 2.0以上WebMvcConfigurerAdapter類就過時了。有以下2中替代方法:
直接實(shí)現(xiàn)org.springframework.web.servlet.config.annotation.WebMvcConfigurer接口。(推薦)
繼承org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport類。但是繼承WebMvcConfigurationSupport會讓SpringBoot對mvc的自動配置失效。不過目前大多數(shù)項(xiàng)目是前后端分離,并沒有對靜態(tài)資源有自動配置的需求,所以繼承WebMvcConfigurationSupport也未嘗不可。
二、HandlerInterceptor 方法介紹
preHandle:預(yù)處理,在業(yè)務(wù)處理器處理請求之前被調(diào)用,可以進(jìn)行登錄攔截,編碼處理、安全控制、權(quán)限校驗(yàn)等處理;
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
postHandle:后處理,在業(yè)務(wù)處理器處理請求執(zhí)行完成后,生成視圖之前被調(diào)用。即調(diào)用了Service并返回ModelAndView,但未進(jìn)行頁面渲染,可以修改ModelAndView,這個比較少用。
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
afterCompletion:返回處理,在DispatcherServlet完全處理完請求后被調(diào)用,可用于清理資源等。已經(jīng)渲染了頁面。
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
三、攔截器(Interceptor)實(shí)現(xiàn)
3.1 實(shí)現(xiàn)HandlerInterceptor
此攔截器演示了通過注解形式,對用戶權(quán)限進(jìn)行攔截校驗(yàn)。
package com.nobody.interceptor;
import com.nobody.annotation.UserAuthenticate;
import com.nobody.context.UserContext;
import com.nobody.context.UserContextManager;
import com.nobody.exception.RestAPIError;
import com.nobody.exception.RestException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
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;
/**
* @Description
* @Author Mr.nobody
* @Date 2020/10/25
* @Version 1.0
*/
@Slf4j
@Component
public class UserPermissionInterceptor implements HandlerInterceptor {
private UserContextManager userContextManager;
@Autowired
public void setContextManager(UserContextManager userContextManager) {
this.userContextManager = userContextManager;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) {
log.info(">>> UserPermissionInterceptor preHandle -- ");
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 獲取用戶權(quán)限校驗(yàn)注解(優(yōu)先獲取方法,無則再從類獲取)
UserAuthenticate userAuthenticate =
handlerMethod.getMethod().getAnnotation(UserAuthenticate.class);
if (null == userAuthenticate) {
userAuthenticate = handlerMethod.getMethod().getDeclaringClass()
.getAnnotation(UserAuthenticate.class);
}
if (userAuthenticate != null && userAuthenticate.permission()) {
// 獲取用戶信息
UserContext userContext = userContextManager.getUserContext(request);
// 權(quán)限校驗(yàn)
if (userAuthenticate.type() != userContext.getType()) {
// 如若不拋出異常,也可返回false
throw new RestException(RestAPIError.AUTH_ERROR);
}
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) {
log.info(">>> UserPermissionInterceptor postHandle -- ");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
log.info(">>> UserPermissionInterceptor afterCompletion -- ");
}
}
3.2 繼承HandlerInterceptorAdapter
package com.nobody.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
/**
* @Description
* @Author Mr.nobody
* @Date 2020/10/25
* @Version 1.0
*/
@Slf4j
@Component
public class UserPermissionInterceptorAdapter extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) {
log.info(">>> UserPermissionInterceptorAdapter preHandle -- ");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) {
log.info(">>> UserPermissionInterceptorAdapter postHandle -- ");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
log.info(">>> UserPermissionInterceptorAdapter afterCompletion -- ");
}
}
四、配置器(WebMvcConfigurer)實(shí)現(xiàn)
4.1 實(shí)現(xiàn)WebMvcConfigurer(推薦)
package com.nobody.config;
import com.nobody.context.UserContextResolver;
import com.nobody.interceptor.UserPermissionInterceptor;
import com.nobody.interceptor.UserPermissionInterceptorAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
/**
* @Description
* @Author Mr.nobody
* @Date 2020/10/25
* @Version 1.0
*/
@Configuration
public class WebAppConfigurer implements WebMvcConfigurer {
private UserPermissionInterceptor userPermissionInterceptor;
private UserPermissionInterceptorAdapter userPermissionInterceptorAdapter;
private UserContextResolver userContextResolver;
@Autowired
public void setUserPermissionInterceptor(UserPermissionInterceptor userPermissionInterceptor) {
this.userPermissionInterceptor = userPermissionInterceptor;
}
@Autowired
public void setUserPermissionInterceptorAdapter(
UserPermissionInterceptorAdapter userPermissionInterceptorAdapter) {
this.userPermissionInterceptorAdapter = userPermissionInterceptorAdapter;
}
@Autowired
public void setUserContextResolver(UserContextResolver userContextResolver) {
this.userContextResolver = userContextResolver;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 可以添加多個攔截器,一般只添加一個
// addPathPatterns("/**") 表示對所有請求都攔截
// .excludePathPatterns("/base/index") 表示排除對/base/index請求的攔截
// 多個攔截器可以設(shè)置order順序,值越小,preHandle越先執(zhí)行,postHandle和afterCompletion越后執(zhí)行
// order默認(rèn)的值是0,如果只添加一個攔截器,可以不顯示設(shè)置order的值
registry.addInterceptor(userPermissionInterceptor).addPathPatterns("/**")
.excludePathPatterns("/base/index").order(0);
// registry.addInterceptor(userPermissionInterceptorAdapter).addPathPatterns("/**")
// .excludePathPatterns("/base/index").order(1);
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(userContextResolver);
}
}
4.2 繼承WebMvcConfigurationSupport
package com.nobody.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import com.nobody.interceptor.UserPermissionInterceptor;
import com.nobody.interceptor.UserPermissionInterceptorAdapter;
/**
* @Description
* @Author Mr.nobody
* @Date 2020/10/25
* @Version 1.0
*/
@Configuration
public class WebAppConfigurerSupport extends WebMvcConfigurationSupport {
@Autowired
private UserPermissionInterceptor userPermissionInterceptor;
// @Autowired
// private UserPermissionInterceptorAdapter userPermissionInterceptorAdapter;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 可以添加多個攔截器,一般只添加一個
// addPathPatterns("/**") 表示對所有請求都攔截
// .excludePathPatterns("/base/index") 表示排除對/base/index請求的攔截
registry.addInterceptor(userPermissionInterceptor).addPathPatterns("/**")
.excludePathPatterns("/base/index");
// registry.addInterceptor(userPermissionInterceptorAdapter).addPathPatterns("/**")
// .excludePathPatterns("/base/index");
}
}
五、其他主要輔助類
5.1 用戶上下文類
package com.nobody.context;
import com.nobody.enums.AuthenticationTypeEnum;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* @Description 用戶上下文
* @Author Mr.nobody
* @Date 2020/10/25
* @Version 1.0
*/
@Getter
@Setter
@ToString
public class UserContext {
// 用戶名稱
private String name;
// 用戶ID
private String userId;
// 用戶類型
private AuthenticationTypeEnum type;
}
5.2 校驗(yàn)訪問權(quán)限注解
package com.nobody.context;
import com.nobody.enums.AuthenticationTypeEnum;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* @Description 用戶上下文
* @Author Mr.nobody
* @Date 2020/10/25
* @Version 1.0
*/
@Getter
@Setter
@ToString
public class UserContext {
// 用戶名稱
private String name;
// 用戶ID
private String userId;
// 用戶類型
private AuthenticationTypeEnum type;
}
5.3 用戶上下文操作類
package com.nobody.context;
import com.nobody.enums.AuthenticationTypeEnum;
import com.nobody.exception.RestAPIError;
import com.nobody.exception.RestException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;
import java.util.UUID;
/**
* @Description 用戶上下文操作類
* @Author Mr.nobody
* @Date 2020/10/25
* @Version 1.0
*/
@Component
public class UserContextManager {
private static final String COOKIE_KEY = "__userToken";
// @Autowired
// private RedisService redisService;
/**
* 獲取用戶上下文信息
*
* @param request
* @return
*/
public UserContext getUserContext(HttpServletRequest request) {
String userToken = getUserToken(request, COOKIE_KEY);
if (!StringUtils.isEmpty(userToken)) {
// 從緩存或者第三方獲取用戶信息
// String userContextStr = redisService.getString(userToken);
// if (!StringUtils.isEmpty(userContextStr)) {
// return JSON.parseObject(userContextStr, UserContext.class);
// }
// 因?yàn)檠菔荆瑳]集成Redis,故簡單new對象
UserContext userContext = new UserContext();
userContext.setName("Mr.nobody");
userContext.setUserId("0000001");
userContext.setType(AuthenticationTypeEnum.ADMIN);
return userContext;
}
throw new RestException(RestAPIError.AUTH_ERROR);
}
public String getUserToken(HttpServletRequest request, String cookieKey) {
Cookie[] cookies = request.getCookies();
if (null != cookies) {
for (Cookie cookie : cookies) {
if (Objects.equals(cookie.getName(), cookieKey)) {
return cookie.getValue();
}
}
}
return null;
}
/**
* 保存用戶上下文信息
*
* @param response
* @param userContextStr
*/
public void saveUserContext(HttpServletResponse response, String userContextStr) {
// 用戶token實(shí)際根據(jù)自己業(yè)務(wù)進(jìn)行生成,此處簡單用UUID
String userToken = UUID.randomUUID().toString();
// 設(shè)置cookie
Cookie cookie = new Cookie(COOKIE_KEY, userToken);
cookie.setPath("/");
response.addCookie(cookie);
// redis緩存
// redisService.setString(userToken, userContextStr, 3600);
}
}
5.4 方法參數(shù)解析器類
package com.nobody.context;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import javax.servlet.http.HttpServletRequest;
/**
* @Description 對有UserContext參數(shù)的接口,進(jìn)行攔截注入用戶信息
* @Author Mr.nobody
* @Date 2020/10/25
* @Version 1.0
*/
@Component
@Slf4j
public class UserContextResolver implements HandlerMethodArgumentResolver {
@Autowired
private UserContextManager userContextManager;
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
log.info(">>> resolveArgument -- begin...");
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
// 從緩存獲取用戶信息賦值到接口參數(shù)中
return userContextManager.getUserContext(request);
}
/**
* 只對UserContext參數(shù)進(jìn)行攔截賦值
*
* @param methodParameter
* @return
*/
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
if (methodParameter.getParameterType().equals(UserContext.class)) {
return true;
}
return false;
}
}
六、測試驗(yàn)證
package com.nobody.controller;
import com.alibaba.fastjson.JSON;
import com.nobody.annotation.UserAuthenticate;
import com.nobody.context.UserContext;
import com.nobody.context.UserContextManager;
import com.nobody.enums.AuthenticationTypeEnum;
import com.nobody.pojo.model.GeneralResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
/**
* @Description
* @Author Mr.nobody
* @Date 2020/10/25
* @Version 1.0
*/
@RestController
@RequestMapping("user")
public class UserController {
@Autowired
private UserContextManager userContextManager;
@GetMapping("login")
public GeneralResult<UserContext> doLogin(HttpServletResponse response) {
UserContext userContext = new UserContext();
userContext.setUserId("0000001");
userContext.setName("Mr.nobody");
userContext.setType(AuthenticationTypeEnum.ADMIN);
userContextManager.saveUserContext(response, JSON.toJSONString(userContext));
return GeneralResult.genSuccessResult(userContext);
}
@GetMapping("personal")
@UserAuthenticate(permission = true, type = AuthenticationTypeEnum.ADMIN)
public GeneralResult<UserContext> getPersonInfo(UserContext userContext) {
return GeneralResult.genSuccessResult(userContext);
}
}
啟動服務(wù)后,在瀏覽器先調(diào)用personal接口,因?yàn)闆]有登錄,所以會報錯沒有權(quán)限:

控制臺輸出:

啟動服務(wù)后,在瀏覽器先訪問login接口進(jìn)行登錄,再訪問personal接口,驗(yàn)證通過,正確返回用戶信息:


七、Github項(xiàng)目
項(xiàng)目工程可從Github獲取,https://github.com/LucioChn/springboot-common.git
到此這篇關(guān)于SpringBoot之HandlerInterceptor攔截器的使用詳解的文章就介紹到這了,更多相關(guān)SpringBoot HandlerInterceptor攔截器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決spring boot創(chuàng)建項(xiàng)目遇到配置的問題
這篇文章主要介紹了解決spring boot創(chuàng)建項(xiàng)目遇到配置的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09
java實(shí)現(xiàn)監(jiān)聽u盤示例分享
這篇文章主要介紹了java實(shí)現(xiàn)監(jiān)聽u盤示例,需要的朋友可以參考下2014-03-03
java并發(fā)編程JUC CountDownLatch線程同步
這篇文章主要介紹CountDownLatch是什么、CountDownLatch 如何工作、CountDownLatch 的代碼例子來展開對java并發(fā)編程JUC CountDownLatch線程同步,需要的朋友可以參考下面文章內(nèi)容2021-09-09
SpringBoot實(shí)現(xiàn)指標(biāo)監(jiān)控
這篇文章主要介紹了SpringBoot實(shí)現(xiàn)指標(biāo)監(jiān)控方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05
在Java中使用redisTemplate操作緩存的方法示例
這篇文章主要介紹了在Java中使用redisTemplate操作緩存的方法示例,在Redis中可以存儲String、List、Set、Hash、Zset。感興趣的可以了解一下2019-01-01
SpringBoot開發(fā)項(xiàng)目,引入JPA找不到findOne方法的解決
這篇文章主要介紹了SpringBoot開發(fā)項(xiàng)目,引入JPA找不到findOne方法的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11
Springboot內(nèi)置tomcat配置虛擬路徑過程解析
這篇文章主要介紹了Springboot內(nèi)置tomcat配置虛擬路徑過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-04-04
分析Java中ArrayList與LinkedList列表結(jié)構(gòu)的源碼
這篇文章主要介紹了Java中ArrayList與LinkedList列表結(jié)構(gòu)的源碼,文章最后對LinkedList和ArrayList以及Vector的特性有一個對比總結(jié),需要的朋友可以參考下2016-05-05

