springboot自定义注解拦截请求指定路径的指定方法上有某个注解就必须带token

 



1.先来自定义一个注解

 



@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Login {
}


 

 

其中注解@Retention可以用来修饰注解,是注解的注解,称为元注解。

按生命周期来划分可分为3类:

1、RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;

2、RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;

3、RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;

这3个生命周期分别对应于:Java源文件(.java文件) —> .class文件 —> 内存中的字节码。

那怎么来选择合适的注解生命周期呢?

首先要明确生命周期长度 SOURCE < CLASS < RUNTIME ,所以前者能作用的地方后者一定也能作用。

 

2,来一个拦截器类

 



@Component
public class AuthorizationInterceptor extends HandlerInterceptorAdapter {
@Autowired
private TokenService tokenService;

public static final String USER_KEY = "userId";

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

//设置跨域--开始
HttpServletResponse httpResponse = (HttpServletResponse) response;
HttpServletRequest httpRequest = (HttpServletRequest) request;
if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
setHeader(httpRequest,httpResponse);
return true;
}
//设置跨域--结束

Login annotation;
if(handler instanceof HandlerMethod) {
annotation = ((HandlerMethod) handler).getMethodAnnotation(Login.class);
}else{
return true;
}

if(annotation == null){
return true;
}

//从header中获取token
String token = request.getHeader("token");
//如果header中不存在token,则从参数中获取token
if(StringUtils.isBlank(token)){
token = request.getParameter("token");
}

//token为空
if(StringUtils.isBlank(token)){
throw new RenException(ErrorCode.TOKEN_NOT_EMPTY);
}

//查询token信息
TokenEntity tokenEntity = tokenService.getByToken(token);
if(tokenEntity == null || tokenEntity.getExpireDate().getTime() < System.currentTimeMillis()){
throw new RenException(ErrorCode.TOKEN_INVALID);
}

//设置userId到request里,后续根据userId,获取用户信息
request.setAttribute(USER_KEY, tokenEntity.getUserId());

return true;
}

/**
* 为response设置header,实现跨域
*/
private void setHeader(HttpServletRequest request,HttpServletResponse response){
//跨域的header设置
response.setHeader("Access-control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Methods", request.getMethod());
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
//防止乱码,适用于传输JSON数据
response.setHeader("Content-Type","application/json;charset=UTF-8");
}

}


 

 

上面有些是数据库的操作, preHandle:在方法被调用前执行。在该方法中可以做类似校验的功能。如果返回true,则继续调用下一个拦截器。如果返回false,则中断执行。

 

3.写一个请求拦截类,让拦截器生效

 



@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private AuthorizationInterceptor authorizationInterceptor;
@Autowired
private LoginUserHandlerMethodArgumentResolver loginUserHandlerMethodArgumentResolver;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authorizationInterceptor).addPathPatterns("/api/**");
}

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(loginUserHandlerMethodArgumentResolver);
}
}


 

 

 

这里拦截了所有请求前面有/api的请求

 

至此自定义注解拦截操作已经做完,在有/api/ * * 的请求,只要加上@Login注解都需要tonken验证,不然就会抛出异常