SpringBoot之HandlerInterceptor攔截器的使用詳解
前言
平常項(xiàng)目開(kāi)發(fā)過(guò)程中,會(huì)遇到登錄攔截,權(quán)限校驗(yàn),參數(shù)處理,防重復(fù)提交等問(wèn)題,那攔截器就能幫我們統(tǒng)一處理這些問(wèn)題。
一、實(shí)現(xiàn)方式
1.1 自定義攔截器
自定義攔截器,即攔截器的實(shí)現(xiàn)類(lèi),一般有兩種自定義方式:
定義一個(gè)類(lèi),實(shí)現(xiàn)org.springframework.web.servlet.HandlerInterceptor接口。
定義一個(gè)類(lèi),繼承已實(shí)現(xiàn)了HandlerInterceptor接口的類(lèi),例如org.springframework.web.servlet.handler.HandlerInterceptorAdapter抽象類(lèi)。
1.2 添加Interceptor攔截器到WebMvcConfigurer配置器中
自定義配置器,然后實(shí)現(xiàn)WebMvcConfigurer配置器。
以前一般繼承org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter類(lèi),不過(guò)SrpingBoot 2.0以上WebMvcConfigurerAdapter類(lèi)就過(guò)時(shí)了。有以下2中替代方法:
直接實(shí)現(xiàn)org.springframework.web.servlet.config.annotation.WebMvcConfigurer接口。(推薦)
繼承org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport類(lèi)。但是繼承WebMvcConfigurationSupport會(huì)讓SpringBoot對(duì)mvc的自動(dòng)配置失效。不過(guò)目前大多數(shù)項(xiàng)目是前后端分離,并沒(méi)有對(duì)靜態(tài)資源有自動(dòng)配置的需求,所以繼承WebMvcConfigurationSupport也未嘗不可。
二、HandlerInterceptor 方法介紹
preHandle:預(yù)處理,在業(yè)務(wù)處理器處理請(qǐng)求之前被調(diào)用,可以進(jìn)行登錄攔截,編碼處理、安全控制、權(quán)限校驗(yàn)等處理;
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
postHandle:后處理,在業(yè)務(wù)處理器處理請(qǐng)求執(zhí)行完成后,生成視圖之前被調(diào)用。即調(diào)用了Service并返回ModelAndView,但未進(jìn)行頁(yè)面渲染,可以修改ModelAndView,這個(gè)比較少用。
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
afterCompletion:返回處理,在DispatcherServlet完全處理完請(qǐng)求后被調(diào)用,可用于清理資源等。已經(jīng)渲染了頁(yè)面。
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
三、攔截器(Interceptor)實(shí)現(xiàn)
3.1 實(shí)現(xiàn)HandlerInterceptor
此攔截器演示了通過(guò)注解形式,對(duì)用戶(hù)權(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;
// 獲取用戶(hù)權(quán)限校驗(yàn)注解(優(yōu)先獲取方法,無(wú)則再?gòu)念?lèi)獲取)
UserAuthenticate userAuthenticate =
handlerMethod.getMethod().getAnnotation(UserAuthenticate.class);
if (null == userAuthenticate) {
userAuthenticate = handlerMethod.getMethod().getDeclaringClass()
.getAnnotation(UserAuthenticate.class);
}
if (userAuthenticate != null && userAuthenticate.permission()) {
// 獲取用戶(hù)信息
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) {
// 可以添加多個(gè)攔截器,一般只添加一個(gè)
// addPathPatterns("/**") 表示對(duì)所有請(qǐng)求都攔截
// .excludePathPatterns("/base/index") 表示排除對(duì)/base/index請(qǐng)求的攔截
// 多個(gè)攔截器可以設(shè)置order順序,值越小,preHandle越先執(zhí)行,postHandle和afterCompletion越后執(zhí)行
// order默認(rèn)的值是0,如果只添加一個(gè)攔截器,可以不顯示設(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) {
// 可以添加多個(gè)攔截器,一般只添加一個(gè)
// addPathPatterns("/**") 表示對(duì)所有請(qǐng)求都攔截
// .excludePathPatterns("/base/index") 表示排除對(duì)/base/index請(qǐng)求的攔截
registry.addInterceptor(userPermissionInterceptor).addPathPatterns("/**")
.excludePathPatterns("/base/index");
// registry.addInterceptor(userPermissionInterceptorAdapter).addPathPatterns("/**")
// .excludePathPatterns("/base/index");
}
}
五、其他主要輔助類(lèi)
5.1 用戶(hù)上下文類(lèi)
package com.nobody.context;
import com.nobody.enums.AuthenticationTypeEnum;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* @Description 用戶(hù)上下文
* @Author Mr.nobody
* @Date 2020/10/25
* @Version 1.0
*/
@Getter
@Setter
@ToString
public class UserContext {
// 用戶(hù)名稱(chēng)
private String name;
// 用戶(hù)ID
private String userId;
// 用戶(hù)類(lèi)型
private AuthenticationTypeEnum type;
}
5.2 校驗(yàn)訪(fǎng)問(wèn)權(quán)限注解
package com.nobody.context;
import com.nobody.enums.AuthenticationTypeEnum;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* @Description 用戶(hù)上下文
* @Author Mr.nobody
* @Date 2020/10/25
* @Version 1.0
*/
@Getter
@Setter
@ToString
public class UserContext {
// 用戶(hù)名稱(chēng)
private String name;
// 用戶(hù)ID
private String userId;
// 用戶(hù)類(lèi)型
private AuthenticationTypeEnum type;
}
5.3 用戶(hù)上下文操作類(lèi)
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 用戶(hù)上下文操作類(lèi)
* @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;
/**
* 獲取用戶(hù)上下文信息
*
* @param request
* @return
*/
public UserContext getUserContext(HttpServletRequest request) {
String userToken = getUserToken(request, COOKIE_KEY);
if (!StringUtils.isEmpty(userToken)) {
// 從緩存或者第三方獲取用戶(hù)信息
// String userContextStr = redisService.getString(userToken);
// if (!StringUtils.isEmpty(userContextStr)) {
// return JSON.parseObject(userContextStr, UserContext.class);
// }
// 因?yàn)檠菔?,沒(méi)集成Redis,故簡(jiǎn)單new對(duì)象
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;
}
/**
* 保存用戶(hù)上下文信息
*
* @param response
* @param userContextStr
*/
public void saveUserContext(HttpServletResponse response, String userContextStr) {
// 用戶(hù)token實(shí)際根據(jù)自己業(yè)務(wù)進(jìn)行生成,此處簡(jiǎ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ù)解析器類(lèi)
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 對(duì)有UserContext參數(shù)的接口,進(jìn)行攔截注入用戶(hù)信息
* @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);
// 從緩存獲取用戶(hù)信息賦值到接口參數(shù)中
return userContextManager.getUserContext(request);
}
/**
* 只對(duì)UserContext參數(shù)進(jìn)行攔截賦值
*
* @param methodParameter
* @return
*/
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
if (methodParameter.getParameterType().equals(UserContext.class)) {
return true;
}
return false;
}
}
六、測(cè)試驗(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);
}
}
啟動(dòng)服務(wù)后,在瀏覽器先調(diào)用personal接口,因?yàn)闆](méi)有登錄,所以會(huì)報(bào)錯(cuò)沒(méi)有權(quán)限:

控制臺(tái)輸出:

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


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

