当前位置:网站首页>自定義HandlerInterceptor攔截器實現用戶鑒權
自定義HandlerInterceptor攔截器實現用戶鑒權
2022-06-29 17:20:00 【擁抱白菜的猪】
攔截器常見的用途有:
1、日志記錄:記錄請求信息的日志,以便進行信息監控、信息統計、計算PV(Page View)等。
2、權限檢查:如登錄檢測,進入處理器檢測檢測是否登錄,如果沒有直接返回到登錄頁面;
3、性能監控:有時候系統在某段時間莫名其妙的慢,可以通過攔截器在進入處理器之前記錄開始時間,在處理完後記錄結束時間,從而得到該請求的處理時間(如果有反向代理,如apache可以自動記錄);
4、通用行為:讀取cookie得到用戶信息並將用戶對象放入請求,從而方便後續流程使用,還有如提取Locale、Theme信息等,只要是多個處理器都需要的即可使用攔截器實現。
5、OpenSessionInView:如Hibernate,在進入處理器打開Session,在完成後關閉Session。
…………本質也是AOP(面向切面編程),也就是說符合橫切關注點的所有功能都可以放入攔截器實現。
攔截器實現用戶鑒權原理:
- 創建用在類上和方法上的自定義注解,用來决定這個方法的訪問是否需要鑒權
- 自定義攔截器,堅定攔截到的請求是否需要鑒權,鑒權是否通過。
- 注册自定義的攔截器
下面以實現HandlerInterceptor攔截器為例
1、自定義注解
/**
* 自定義注解,用來錶示方法活類是否需要鑒權
*/
@Target({ElementType.METHOD, ElementType.TYPE}) //指定注解的使用比特置
@Retention(RetentionPolicy.RUNTIME) //指定注解的作用範圍,代碼運行時
public @interface UserLoginToken {
boolean required() default true; //默認為TRUE
}2、自定義攔截器,實現HandlerInterceptor接口
public class AuthenticationInterceptor implements HandlerInterceptor {
/**
* 該方法將在請求處理之前進行調用,用來堅定用戶權限
*/
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
String token = httpServletRequest.getHeader("token");// 從 http 請求頭中取出 token
// 如果不是映射到方法直接通過
if(!(object instanceof HandlerMethod)){
return true;
}
//獲取請求的方法
HandlerMethod handlerMethod=(HandlerMethod)object;
Method method=handlerMethod.getMethod();
//檢查是否有passtoken注釋,有則跳過認證
if (method.isAnnotationPresent(PassToken.class)) {
PassToken passToken = method.getAnnotation(PassToken.class);
if (passToken.required()) {
return true;
}
}
//檢查有沒有需要用戶權限的注解
if (method.isAnnotationPresent(UserLoginToken.class)) {
UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
if (userLoginToken.required()) {
// 執行認證
if (token == null) {
log.error("無token,請重新登錄");
throw new RuntimeException("無token,請重新登錄");
}
// 獲取 token 中的 user id
String userId;
try {
DecodedJWT decode = JWT.decode(token);
userId = decode.getAudience().get(0);
Date expiresAt = decode.getExpiresAt();//獲取token過期時間
if (expiresAt.before(new Date())) {
log.error("token已過期,請重新登錄");
throw new RuntimeException("token已過期,請重新登錄");
}
} catch (Exception j) {
log.error("無token,請重新登錄");
throw new RuntimeException("無token,請重新登錄");
}
//根據從token中解析的userID判斷用戶是否存在
// ExTraderUser user = exTraderUserMapper.selectByCode(userId);
// ExUser exUserV2 = exUserMapper.selectByCode(userId);
// if (user == null && exUserV2 == null) {
// log.error("用戶不存在,請重新登錄");
// throw new RuntimeException("用戶不存在,請重新登錄");
// }
// if(Objects.isNull(user) && Objects.isNull(user.getTRADE_CENTER_NAME()) &&Objects.isNull(exUserV2) && Objects.isNull(exUserV2.getTRADE_CENTER_NAME())){
// log.error("用戶不存在,請重新登錄");
// throw new RuntimeException("用戶不存在,請重新登錄");
// }
try {
// 驗證 token
if(user != null){
// JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getTRADE_CENTER_NAME())).build();
// jwtVerifier.verify(token); //根據令牌和簽名解析數據
}
// if(exUserV2 != null){
// JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(exUserV2.getTRADE_CENTER_NAME())).build();
// jwtVerifier.verify(token); //根據令牌和簽名解析數據
}
} catch (Exception e) {
log.error("無token,請重新登錄");
throw new RuntimeException("無token,請重新登錄");
}
// UserConfig.setUser(exUserV2);
return true;
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
UserConfig.removeUser();
}
}3、注册自定義攔截器
有了攔截器PasswordStateInterceptor,還需要對攔截器進行注册。需要使用WebMvcConfigurerAdapter 下的addInterceptors方法。 新建一個類WebConfigfilter.java,繼承自WebMvcConfigurerAdapter 。
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
/**
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authenticationInterceptor())
.addPathPatterns("/**"); // 攔截所有請求,通過判斷是否有 @LoginRequired 注解 决定是否需要登錄
}
@Bean
public AuthenticationInterceptor authenticationInterceptor() {
return new AuthenticationInterceptor();
}
}攔截器HandlerInterceptor有三個方法:
- preHandle
- postHandle
- afterCompletion
(1)preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法,顧名思義,該方法將在請求處理之前進行調用。SpringMVC 中的Interceptor 是鏈式的調用的,在一個應用中或者說是在一個請求中可以同時存在多個Interceptor 。每個Interceptor 的調用會依據它的聲明順序依次執行,而且最先執行的都是Interceptor 中的preHandle 方法,所以可以在這個方法中進行一些前置初始化操作或者是對當前請求的一個預處理,也可以在這個方法中進行一些判斷來决定請求是否要繼續進行下去。該方法的返回值是布爾值Boolean 類型的,當它返回為false 時,錶示請求結束,後續的Interceptor 和Controller 都不會再執行;當返回值為true 時就會繼續調用下一個Interceptor 的preHandle 方法,如果已經是最後一個Interceptor 的時候就會是調用當前請求的Controller 方法。
(2)postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法,由preHandle 方法的解釋我們知道這個方法包括後面要說到的afterCompletion 方法都只能是在當前所屬的Interceptor 的preHandle 方法的返回值為true 時才能被調用。postHandle 方法,顧名思義就是在當前請求進行處理之後,也就是Controller 方法調用之後執行,但是它會在DispatcherServlet 進行視圖返回渲染之前被調用,所以我們可以在這個方法中對Controller 處理之後的ModelAndView 對象進行操作。postHandle 方法被調用的方向跟preHandle 是相反的,也就是說先聲明的Interceptor 的postHandle 方法反而會後執行,這和Struts2 裏面的Interceptor 的執行過程有點類型。Struts2 裏面的Interceptor 的執行過程也是鏈式的,只是在Struts2 裏面需要手動調用ActionInvocation 的invoke 方法來觸發對下一個Interceptor 或者是Action 的調用,然後每一個Interceptor 中在invoke 方法調用之前的內容都是按照聲明順序執行的,而invoke 方法之後的內容就是反向的。
(3)afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法,該方法也是需要當前對應的Interceptor 的preHandle 方法的返回值為true 時才會執行。顧名思義,該方法將在整個請求結束之後,也就是在DispatcherServlet 渲染了對應的視圖之後執行。這個方法的主要作用是用於進行資源清理工作的。
边栏推荐
- In depth analysis of Monai (I) data and transforms
- Gradle download slow or unable to download
- 序列检测器
- Why is informatization ≠ digitalization? Finally someone made it clear
- 使用 SSH 方式拉取代码
- Which is better and safer, GF e-gold or Dongfang fortune
- [the sixth operation of modern signal processing]
- R语言epiDisplay包的aggregate函数将数值变量基于因子变量拆分为不同的子集,计算每个子集的汇总统计信息、aggregate.data.frame函数包含缺失值的情况下分组统计结果为NA
- epoll分析
- controller、service、dao之间的关系
猜你喜欢

PCB板框的绘制——AD19

机器学习7-支持向量机

基于C语言开发实现的一个用户级线程库

In depth analysis of Monai (I) data and transforms

Étalonnage de la caméra monoculaire et de la caméra binoculaire à l'aide de l'outil d'étalonnage kalibr

知道创宇为能源行业资产管理助力,入选工信部2021物联网示范项目

腾讯云发布自动化交付和运维产品Orbit,推动企业应用全面云原生化

深圳内推 | 深圳计算科学研究院招聘机器学习助理工程师(校招)

mysql在linux中2003错误如何解决

windows平台下的mysql启动等基本操作
随机推荐
ICML 2022 | transferable imitation learning method based on decoupling gradient optimization
在供应链场景应用中,供应链管理系统扮演什么角色?
最高81.98%!超百所“双一流”高校本科深造率公布
mysql查询视图命令是哪个
Solid state storage manufacturer Yilian joins dragon dragon community to build a new open source ecosystem
开源仓库贡献 —— 提交 PR
Why is informatization ≠ digitalization? Finally someone made it clear
微博评论高性能高可用架构设计(架构实战营 模块五作业)
首批!腾讯云通过中国信通院政务协同平台解决方案能力评估
【Oracle】基础知识面试题
使用 SSH 方式拉取代码
sectigo ov泛域名证书一年一千五百九十元好用吗
Go语言多方式并发实现龟兔赛跑
深圳内推 | 深圳计算科学研究院招聘机器学习助理工程师(校招)
Collaborative development of epidemic home outsourcing project 𞓜 community essay solicitation
浏览器大尺寸截屏
在线文本数字识别列表求和工具
curl: (56) Recv failure: Connection reset by peer
C语言练习----指针字符串、链表
正则表达式