拦截器
注册拦截器
InterceptorConfig.java
package com.example.springboot.sercurity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Component
public class InterceptorConfig implements WebMvcConfigurer {
@Autowired
private AuthInterceptor authInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 拦截所有请求
registry.addInterceptor(authInterceptor).addPathPatterns("/api/**");
}
}
拦截器执行过程
- 1.preHandle
- 2.执行处理器逻辑(return前的语句)
- 3.postHandle
- 4.视图解析和试图渲染(return的页面)
- 5.afterCompletion
自定义拦截器
AuthInterceptor.java
package com.example.springboot.sercurity;
import com.example.springboot.service.UserService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import java.lang.reflect.Method;
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Autowired
private UserService userService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception{
// 获取token
String token = request.getHeader("token");
//如果不是映射到Controller直接放行
//如果没有这句类型判断,当处理跨域请求时,类型转换会报错
if(!(object instanceof HandlerMethod handlerMethod)){
return true;
}
// 检查是否需要验证token
Method method = handlerMethod.getMethod();
//是否存在VerifyAdminToken注解,这个注解后文会说明
if(method.isAnnotationPresent(VerifyAdminToken.class)){
// 获取注解,验证token是否符合注解要求
String str = checkToken(method.getAnnotation(VerifyAdminToken.class),token);
if(!StringUtils.isEmpty(str)) throw new RuntimeException(str);
return true;
}
return true;
}
// 检验token
public String checkToken(VerifyAdminToken loginToken,String token){
if(loginToken.required()){
if(token == null)return "该请求没有token,请先获取token";
if(userService.GetUserByToken(token)==null)return "未找到持有该token的用户";
if(userService.GetUserByToken(token).getLevel()>=loginToken.level()){// token用户等级大于有效,判断是否要刷新令牌
return null;
}else{
return "无效令牌";
}
}
return null;
}
}
代码中的
if(!(object instanceof HandlerMethod handlerMethod)){
return true;
}
// 检查是否需要验证token
Method method = handlerMethod.getMethod();
原本是这句
HandlerMethod handlerMethod = (HandlerMethod)object;
跨域请求就会类型转换错误
java.lang.ClassCastException: class org.springframework.web.servlet.handler.AbstractHandlerMapping$PreFlightHandler cannot be cast to class org.springframework.web.method.HandlerMethod (org.springframework.web.servlet.handler.AbstractHandlerMapping$PreFlightHandler and org.springframework.web.method.HandlerMethod are in unnamed module of loader 'app')
当加入@CrossOrigin后,传到拦截器的object类型改变
多个拦截器的顺序
处理器前方法先注册先执行,处理器后方法和完成方法先注册后执行。若返回false,后续的处理器后方法和目标方法都不会执行,完成方法只执行返回true的拦截器的完成方法,而且顺序是先注册后执行。
自定义注解
注解(Annotation)是 Java 语言中的一个特性,用于为程序元素(类、方法、成员变量等)添加额外的信息,提供元数据。注解本身并不提供实际的逻辑功能,而是为其他工具或框架提供信息。
这是一个自定义注解 VerifyAdminToken,用于标识需要进行管理员令牌验证的类或方法。
VerifyAdminToken.java
package com.example.springboot.sercurity;
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 VerifyAdminToken {
boolean required() default true;
int level() default 6;
}
@Target({ElementType.METHOD, ElementType.TYPE}) 表示这个注解可以标注在方法和类上。
@Retention(RetentionPolicy.RUNTIME) 表示这个注解将被保留到运行时。这样,在运行时可以通过反射获取到这个注解的信息。
boolean required() default true; 定义了一个必需的属性 required,默认值为 true。表示是否需要进行管理员令牌验证。
int level() default 6; 定义了一个可选的属性 level,默认值为 6。表示验证的级别。
通过method.getAnnotation(VerifyAdminToken.class)获取到对象的注解