拦截器的使用

拦截器(Interceptor)是一种在应用程序中用于干预、修改或拦截请求和响应的组件,是AOP 编程的一种实践,和过滤器一样都是一种具体的AOP实现。它可以在请求被发送到目标处理程序之前或之后,对请求进行预处理或对响应进行后处理。

拦截器通常用于以下目的:

  1. 认证和授权:拦截器可以对请求进行身份验证和权限检查,确保只有经过认证和授权的用户才能访问特定的资源或执行特定的操作。

  2. 日志记录和性能监控:拦截器可以用于记录请求和响应的日志,以便进行故障排查、性能监控和分析。

  3. 请求转发和重定向:拦截器可以根据特定的条件,对请求进行转发或重定向至不同的处理程序或页面。

  4. 数据转换和格式化:拦截器可以对请求和响应的数据进行转换、格式化或验证,以满足特定的需求或规范。

在很多框架和应用程序中都有拦截器的概念,比如在Java中的Spring MVC框架中,可以使用拦截器来拦截和处理HTTP请求;在网络安全领域,防火墙和入侵检测系统也可以使用拦截器来拦截和过滤恶意流量。拦截器在应用程序中起到了很重要的作用,增强了系统的安全性、可靠性和可控性。

1.拦截器的编写方法 

拦截器的编写比较简单,一般是根据自己的不同需要继承不同的拦截器基类。大致分为两类,一类是基于业务判断服务的拦截器,比如:日志服务,权限认证,这种拦截器一般继承自 HandlerInterceptor类;另一类是和配置有关的拦截器,一般继承自 WebMvcConfigurer 。其中第一类最多,需要重写 preHandle 和 postHandle 方法,其中 preHandle 方式用于请求执行前的时候,当preHandle 返回  true 才能进行下一个方法,而 postHandle 是作用于请求结束前。afterCompletion 是视图显示完成后才会执行。 

第一类,基于 HandlerNterceptor 类的拦截器 ,用于日志服务:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;@Component
public class LoggerInterceptor extends HandlerInterceptorAdapter {//生成日志类private Logger logger = LoggerFactory.getLogger(LoggerInterceptor.class);@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {HandlerMethod handlerMethod = (HandlerMethod) handler;// Returns the name of the HTTP method with which this request was made, for example, GET, POST, or PUT.Method method = handlerMethod.getMethod();//得到方法信息//从当前方法中获取一个日志注解对象LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);if (logAnnotation != null){long startTime = System.currentTimeMillis();request.setAttribute("startTime",startTime);//设置属性logger.info("enter"+method.getName()+"method cost time is:"+startTime+" ms");}return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();//得到方法信息LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);if (logAnnotation != null){long endTime = System.currentTimeMillis();long startTime = (long) request.getAttribute("startTime");long periodTime = endTime- startTime;logger.info("Leave "+method.getName()+"method time is:"+endTime+" ms");logger.info("On "+method.getName()+"method cost time is:"+periodTime+" ms");}}
}

将分别计算并输出进入某个方法和离开某个方法的时间。 

声明注解 :在需要使用该服务的方法上加上该注解即可使用

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface LogAnnotation {String message() default "666";String[] params() default {};}

注册配置拦截器:实现WebMvcConfigurer接口,并重写addInterceptors方法

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.WebMvcConfigurer;@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate LoggerInterceptor loggerInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loggerInterceptor).addPathPatterns("/**").excludePathPatterns("/ls");//不拦截的路径}
}

2.邮箱有效性验证 

常见的邮箱如 qq,网易,地址形式如 xxx@qq.com ,xxx@163.com .

总结成正则表达式如下:

"^([a-z0-9A-z]+[-|\\.]?)+[a-z0-9A-z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+([a-zA-Z]{2,})$"

用Java编写一个检查有效性的例子:

 public static boolean matchTest(String email){boolean a = email.matches("^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+([a-zA-Z]{2,})$");System.out.println(a);return a;}

编写完整的拦截器:

import org.example.service.Testjson;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class EmailInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String email = request.getParameter("email");if (Testjson.matchTest(email)){return true;}else {return false;}}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}

这里只需要重写 preHandle 方法即可,完成对有效性邮箱地址的判断效果。

3.Token 检查拦截器实现

Token检查是一个健全系统或者服务必须有的一项安全性功能,特别是基于 RESTful 的接口,需要检查Token来判断用户是否已经登陆或者验证身份,利用Token可以获取进一步的网络资源和权限。

一般我们会使用 JWT 验证方式,JSON Web Token 简称 JWT。本质上是一段字符串,由以下三个部分构成。

  • 头部: JWT 基本信息,包括算法。
  • 荷载: 自定义数据,包括用户标识,如 userid。
  • 签名: 前面两者通过一定加密算法后计算所得其签名字符串。

引入 JWT 依赖到 pom.xml 文件中,代码如下:

        <dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.4.0</version></dependency>

更新前端拦截器配置,处理所有请求:

@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate LoggerInterceptor loggerInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loggerInterceptor).addPathPatterns("/**").excludePathPatterns("/ls");//不拦截的路径registry.addInterceptor(authenticationInterceptor()).addPathPatterns("/**");}@Beanpublic AuthenticationInterceptor authenticationInterceptor(){return new AuthenticationInterceptor();}
}

新建两个注解,用于判断是否进行Token验证,首先编写需要登陆验证的注解UserLoginToken:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 需要登陆才能操作得注解*/
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {boolean required() default true;
}

用于跳过验证的注解:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {boolean required() default true;
}

检查Token的拦截器:

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import org.example.Exception.BusinessException;
import org.example.pojo.User;
import org.example.service.UserService;
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;
import java.lang.reflect.Method;@Component
public class AuthenticationInterceptor implements HandlerInterceptor {@AutowiredUserService userService;@AutowiredRedisUtil redisUtil;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String token = request.getHeader("token");//排除访问的是静态资源,而不是映射访问if (!(handler instanceof HandlerMethod)){return true;}//获取访问的方法HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();//是否被跳过验证注解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){throw new BusinessException("4001","no token");}String userId;try{userId = JWT.decode(token).getAudience().get(0);}catch (Exception e){throw new BusinessException("4003","decode token fails");}//Check the expire of tokenString tokenKey = userId+":"+token;boolean hasExisted = redisUtil.hasKey(tokenKey);System.out.println("exit or not: "+hasExisted);if (!hasExisted){throw new BusinessException("4005","token expired!");}int userID = Integer.parseInt(userId);System.out.println("userId is:"+userID);User user =userService.findUserById(userID);if (user == null){throw new RuntimeException("user not exits");}try {JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword()+"MTest!76&sQ^")).build();jwtVerifier.verify(token);//设置当前登录用户LoginUser loginUser = new LoginUser();loginUser.setID((long)userID);UserContext.setUser(loginUser);}catch (JWTVerificationException e){throw new BusinessException("4002","invalid token");}}}return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}

使用起来也很方便,在需要与不需要进行 Token 验证的接口前面加上对应的注释即可。

补充:

 handler 参数是 Spring MVC 框架传递给你的 preHandle 方法的一个对象,它代表当前请求即将要调用的控制器逻辑。在 Spring MVC 中,这个 handler 对象可以是多种类型,但通常它是 HandlerMethod 的一个实例

当一个请求被映射到某个控制器的方法时,这个方法就被包装为 HandlerMethod 对象。HandlerMethod 对象包含了这个方法以及包含这个方法的控制器的信息,允许你在拦截器中对即将执行的控制器方法进行一些前处理。

在某些情况下,handler 可能不是 HandlerMethod 的实例。例如,当请求是针对静态资源时,或者是某个请求没有被映射到任何的控制器上的方法时handler 可能是框架中其他类型的处理器,这取决于你的应用配置和请求的具体类型。这就是为什么在 preHandle 方法中进行 instanceof HandlerMethod 检查是一种常见的做法,以确保你正在处理的是一个具体的控制器方法。如果是,则可以安全地转型到 HandlerMethod 并进行进一步的处理。

在 preHandle 方法的上下文中,你可以对 handler 参数进行各种检查和操作。例如,你可以检查用户是否有权限执行与 handler 对应的控制器方法,你可以修改请求或响应,或者决定是否继续执行请求处理链(通过返回 true 或者 false)。

在Spring MVC框架中:

静态资源请求:
  这些请求通常不需要经过复杂的处理,它们被映射到服务器上存储静态文件的目录。例如在Spring Boot中,静态资源可以放置在 `/static`、`/public` 等默认的资源目录中,这些目录中的资源可以直接通过URL被访问。在这种情况下,与静态资源对应的`handler`可能就不是一个`HandlerMethod`,而是其他用于处理静态资源的类的实例。

数据请求:
  这些请求需要后端处理,如执行数据库操作、进行计算或调用其他接口等。在Spring MVC中,这些请求被映射到控制器类中的特定方法上,这些方法通过注解(如`@RequestMapping`或其特化版本比如`@GetMapping`、`@PostMapping`等)来标注。这时候传递给`preHandle`方法的`handler`参数就是一个`HandlerMethod`实例,代表被映射的那个方法。

因此,在拦截器的`preHandle`方法中,进行`handler instanceof HandlerMethod`检查可以帮助你明确该次请求是需要Spring MVC通过控制器中的方法处理,还是仅仅是为了获取静态资源。这可以确保你的拦截器逻辑只应用于实际需要拦截的后端处理请求,而不是静态资源请求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/165654.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构】二叉树概念 | 满二叉树 | 完全二叉树

二叉树的概念 二叉树在实践中用的很多。 一棵二叉树是结点的一个有限集合&#xff0c;该集合&#xff1a; 或者为空&#xff1b;由一个根结点加上两棵别称为左子树和右子树的二叉树组成。二叉树最多两个孩子。 这里注意&#xff1a;二叉树并不是度为2的树。 二叉树的度最大值是…

Go lumberjack 日志轮换和管理

在开发应用程序时&#xff0c;记录日志是一项关键的任务&#xff0c;以便在应用程序运行时追踪问题、监视性能和保留审计记录。Go 语言提供了灵活且强大的日志记录功能&#xff0c;可以通过多种方式配置和使用。其中一个常用的日志记录库是 github.com/natefinch/lumberjack&am…

python selenium 模拟浏览器自动操作抢购脚本

每逢秒杀&#xff0c;都在遗憾网速和手速慢没能抢购到商品吧。 手写一个脚本&#xff0c;让程序帮你抢&#xff0c;抢到的概率会大大提升。 废话不多说&#xff0c;直接上代码。 本实例以华为官网抢购手机为例 """ 模拟浏览器操作华为官网(1) 【只需要安装一…

【JAVA】我们该如何规避代码中可能出现的错误?(二)

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️初识JAVA】 文章目录 前言异常方法&#xff08;Throwable类&#xff09;Throwable类的方法 捕获异常多重捕获块 前言 异常是程序中的一些错误&#xff0c;但并不是所有的错误都是异常&#xff0c;并…

git-3

1.如何让工作区的文件恢复为和暂存区一样&#xff1f; 工作区所作的变更还不及暂存区的变更好&#xff0c;想从暂存区拷贝到工作区&#xff0c;变更工作区(恢复成和暂存区一样的状态)&#xff0c;想到用git checkout -- 文件名 2.怎样取消暂存区部分文件的更改&#xff1f; 如…

无损压缩技巧:减小PDF文件尺寸的有效方法

我们在制作pdf文档的时候&#xff0c;会加入许多内容&#xff0c;文字、图片等等&#xff0c;素材添加的过多之后就会导致pdf文档特别大&#xff0c;在上传或者储存时&#xff0c;就会特别不方便&#xff0c;所以今天就告诉大家一个pdf压缩的方法&#xff0c;使用pdf在线压缩工…

4-Docker命令之docker info

后续为大家逐个讲解一下docker常用命令及其相关用法。docker常用命令查看如下: [root@centos79 ~]# docker --helpUsage: docker [OPTIONS] COMMANDA self-sufficient runtime for containersCommon Commands:run Create and run a new container from an imageexec…

洛谷 P1883 函数

P1883 函数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) Error Curves - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 这两题是一模一样的&#xff0c;过一题水两题。 分析 主要难点在于证明F(x)是一个单峰函数可以被三分&#xff0c;但是我随便画了几个f(x)之后发现好像…

MySQL的Redo Log跟Binlog

文章目录 概要Redo Log日志Redo Log的作用Redo Log的写入机制 Binlog日志Binlog的作用Binlog写入机制 两段提交 概要 Redo Log和Binlog是MySQL日志系统中非常重要的两种机制&#xff0c;也有很多相似之处&#xff0c;本文主要介绍两者细节和区别。 Redo Log日志 Redo Log的作…

Docker+ Jenkins+Maven+git自动化部署

环境&#xff1a;Centos7 JDK1.8 Maven3.3.9 Git 2.40 Docker 20.10.17 准备工作&#xff1a; 安装Docker Centos7默认的yum安装的docker是1.13&#xff0c;版本太低&#xff0c;很多镜像都要Docker版本要求&#xff0c;升级Docker版本。 卸载已安装Docker: yum …

你知道如何实现游戏中的透视效果吗?

引言 游戏中的透视效果可以合理运用CtrlCV实现。 不知道大家有没有这样一段经历&#xff1a;在做Cocos项目时需要一些特定的Shader去做一些特定的效果&#xff0c;例如透视、高光、滤镜等等&#xff0c;想自己写吧&#xff0c;不怎么会啊&#xff0c;网上又找不到&#xff0c…

27 - 如何使用设计模式优化并发编程?

在我们使用多线程编程时&#xff0c;很多时候需要根据业务场景设计一套业务功能。其实&#xff0c;在多线程编程中&#xff0c;本身就存在很多成熟的功能设计模式&#xff0c;学好它们&#xff0c;用好它们&#xff0c;那就是如虎添翼了。今天我就带你了解几种并发编程中常用的…

redis-cluster集群(目的:高可用)

1、特点 集群由多个node节点组成&#xff0c;redis数据分布在这些节点中&#xff0c;在集群中分为主节点和从节点&#xff0c;一个主对应一个从&#xff0c;所有组的主从形成一个集群&#xff0c;每组的数据是独立的&#xff0c;并且集群自带哨兵模式 2、工作原理 集群模式中…

【ZedBoard学习实例1】 VGA显示彩条

ZedBoard学习实例1 VGA显示彩条 ZedBoard学习实例1 VGA显示彩条参考文章改进 ZedBoard学习实例1 VGA显示彩条 参考文章 彩条控制verilog代码 主体参考了该文章的代码&#xff0c;文中还介绍了相关的电路图&#xff0c;还有ZedBoard的手册内容。19201080分辨率显示器的参数 针…

重生之我是一名程序员 37 ——C语言中的栈溢出问题

哈喽啊大家晚上好&#xff01; 今天呢给大家带来一个烧脑的知识——C语言中的栈溢出问题。那什么是栈溢出呢&#xff1f;栈溢出指的是当程序在执行函数调用时&#xff0c;为了保护函数的局部变量和返回地址&#xff0c;将这些数据存储在栈中。如果函数在函数调用时使用了过多的…

Sentinel核心类解读:Entry

默认情况下&#xff0c;Sentinel会将controller中的方法作为被保护资源&#xff0c;Sentinel中的资源用Entry来表示。 Sentinel中Entry可以理解为每次进入资源的一个凭证&#xff0c;如果调用SphO.entry()或者SphU.entry()能获取Entry对象&#xff0c;代表获取了凭证&#xff…

安卓手机便签APP用哪个,手机上好用的便签APP是什么

在日常生活及工作方面&#xff0c;总是有许多做不完的事情需要大家来处理&#xff0c;当多项任务堆叠交叉在一起时&#xff0c;很容易漏掉一些项目&#xff0c;这时候大家会借助经常携带的手机来记录容易忘记的事情&#xff0c;如手机上的闹钟、定时提醒软件都可以用来记录待办…

2023亚太杯数学建模A题思路分析 - 采果机器人的图像识别技术

1 赛题 问题A 采果机器人的图像识别技术 中国是世界上最大的苹果生产国&#xff0c;年产量约为3500万吨。与此同时&#xff0c;中国也是世 界上最大的苹果出口国&#xff0c;全球每两个苹果中就有一个&#xff0c;全球超过六分之一的苹果出口 自中国。中国提出了一带一路倡议…

JDK11新特性

目录 一、JShell 二、Dynamic Class-File Constants类文件新添的一种结构 三、局部变量类型推断&#xff08;var ”关键字”&#xff09; 四、新加的一些实用API 1. 新的本机不可修改集合API 2. Stream 加强 3. String 加强 4. Optional 加强 5. 改进的文件API 五、移…

canvas

Canvas 是 Android 中用于绘制图形的重要类&#xff0c;它提供了许多用于绘制的常用方法。以下是一些常用的 Canvas 方法&#xff1a; 绘制颜色和背景&#xff1a; drawColor(int color): 用指定颜色填充整个画布。drawRGB(int r, int g, int b): 用 RGB 值指定颜色填充整个画布…