在第一篇中(https://blog.csdn.net/qq_39526250/article/details/80782864)我们已经搭建了基础的springboot项目讲述了搭建基本的springboot项目+mybatis+mysql数据库,详细的增删改查就不再写了,不知道的可以去百度或者提问。本文章开始讲述自定义拦截器的类容。
1.首先创建拦截器 AuthInterceptor 继承字 HandlerInterceptorAdapter 并重写 preHandle 方法
package com.bctc.servlet.Intercept; import com.bctc.entity.SMKeyBeen; import com.bctc.service.SSLService; import com.bctc.servlet.redis.RedisUtils; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.context.support.WebApplicationContextUtils; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.lang.reflect.Method; /** * 添加拦截器 AuthInterceptor 继承字 HandlerInterceptorAdapter 重写 preHandle 方法 * 所有请求先进来这里 */ @Component public class AuthInterceptor extends HandlerInterceptorAdapter { private Logger log = Logger.getLogger(AuthInterceptor.class); @Autowired private SSLService sSLService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { log.info("--进入拦截器AuthInterceptor--"); if (!HandlerMethod.class.isAssignableFrom(handler.getClass())) { log.info("--进入拦截器1,是用来判断一个类Class1和另一个类Class2是否相同或是另一个类的子类或接口。 "); return true; } // 如果不是映射到方法直接通过 /* if (!(handler instanceof HandlerMethod)) { log.info("--进入拦截器,但该方法不需要拦截,用来判断一个对象实例是否是一个类或接口的或其子类子接口的实例。 "); return true; }*/ HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); String requestPath = request.getRequestURI(); // log.debug("requestIp: " + getIpAddress(request)); //log.debug("Method: " + method.getName() + ", OenIntercept: " + method.isAnnotationPresent(OenIntercept.class)); //log.debug("requestPath: " + requestPath); if (!handlerMethod.hasMethodAnnotation(OenIntercept.class) && handlerMethod.getBeanType().getAnnotation(OenIntercept.class) == null && !handlerMethod.hasMethodAnnotation(TwoIntercept.class) && handlerMethod.getBeanType().getAnnotation(TwoIntercept.class) == null) { log.info("--进入拦截器,类和方法上都没有拦截注解"); return true; } if (requestPath.contains("/111") || requestPath.contains("/swagger") || requestPath.contains("/configuration/ui")) { log.info("--1123"); return true; } if (requestPath.contains("/error")) { log.info("--1111333"); return true; } if (method.isAnnotationPresent(OenIntercept.class) || handlerMethod.hasMethodAnnotation(OenIntercept.class)) { log.info("---OenIntercept----"); String token = request.getHeader("ACCESS_TOKEN"); token = "P100AFH00W"; log.debug("token: " + token); if (token.equals("") || token == null) { throw new Exception("无效token"); } SMKeyBeen ssLs = sSLService.getSSLs(token); log.info(ssLs.getId()); request.setAttribute("currentUser", ssLs); RedisUtils redisUtils = WebApplicationContextUtils .getWebApplicationContext(request.getServletContext()).getBean(RedisUtils.class); redisUtils.set("niuhao", ssLs); return true; } if (method.isAnnotationPresent(TwoIntercept.class) || handlerMethod.hasMethodAnnotation(TwoIntercept.class)) { log.info("---TwoIntercept----"); String token = request.getHeader("ACCESS_TOKEN"); token = "P100AFH00W"; log.debug("token: " + token); if (token.equals("") || token == null) { throw new Exception("无效token"); } SMKeyBeen ssLs = sSLService.getSSLs(token); log.info(ssLs.getId()); ssLs.setId(123456); request.setAttribute("TwoCurrent", ssLs); return true; } log.info("-离开拦截器"); return true; } } /*Class.isAssignableFrom()方法 与 instanceof 关键字的区别 1. Class.isAssignableFrom() 是用来判断一个类Class1和另一个类Class2是否相同或是另一个类的子类或接口。 格式为: Class1.isAssignableFrom(Class2) 调用者和参数都是java.lang.Class类型, 返回值:boolean类型 以:Object,String为例 a. "String是Object的父类" String.class.isAssignableFrom(Object.class) 结果false; b. "Object是String的父类" Object.class.isAssignableFrom(String.class) 结果true; c. "Object和Object相同" Object.class.isAssignableFrom(Object.class) 结果true; 2. instanceof 用来判断一个对象实例是否是一个类或接口的或其子类子接口的实例。 格式是:o instanceof TypeName 第一个参数是对象实例名,第二个参数是具体的类名或接口名。 以:Object,String为例 String str = new String(); String obj = new Object(); a. "Object是String的父类" str instanceof Object 结果true; b. "String和String相同" str instanceof String 结果true; */
2.OenIntercept第一个拦截注解
package com.bctc.servlet.Intercept; import java.lang.annotation.*; /** * 第一个拦截注解 */ @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface OenIntercept { }
3.OenCurrent 用于标识用户实体类入参,参数级注解
package com.bctc.servlet.Intercept; import java.lang.annotation.*; @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) @Documented //@OenCurrent 用于标识用户实体类入参,参数级注解 public @interface OenCurrent { }
4.添加参数解析器 OenCurrentMethodArgumentResolver package com.bctc.servlet.Intercept; import com.bctc.entity.SMKeyBeen; import org.springframework.core.MethodParameter; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.multipart.support.MissingServletRequestPartException; // 添加参数解析器 OenCurrentMethodArgumentResolver public class OenCurrentMethodArgumentResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { System.out.println("--OenCurrentMethodArgumentResolver--"); return parameter.getParameterType().isAssignableFrom(SMKeyBeen.class) && parameter.hasParameterAnnotation(OenCurrent.class); } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { SMKeyBeen userInfo = (SMKeyBeen) webRequest.getAttribute("currentUser", RequestAttributes.SCOPE_REQUEST); if (userInfo != null) { System.out.println(userInfo.getId()+"----"); return userInfo; } throw new MissingServletRequestPartException("currentUser"); } } |
5.然后就是在MyWebMvcConfigurerAdapter配置拦截器(将下面内容复制进去就可以
)
package com.bctc.servlet.Intercept;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.util.List;
/**
* springboot1.0 继承WebMvcConfigurerAdapter
* 注册拦截器和参数解析器在 WebMvcConfigurerAdapter 中,需要注意,拦截器中引用了 UserService 所以在注册时需要使用 @Bean 的形式以告诉 Spring 注入
*/
@Configuration
public class MyWebMvcConfigurerAdapter extends WebMvcConfigurerAdapter {
//关键,将拦截器作为bean写入配置中
@Bean
public AuthInterceptor myAuthInterceptor() {
return new AuthInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册拦截器
InterceptorRegistration ir = registry.addInterceptor(myAuthInterceptor());
// 配置不拦截的路径
ir.excludePathPatterns("/RemotecontrolController/*");
// 配置拦截的路径
ir.addPathPatterns("/**");
// 还可以在这里注册其它的拦截器
//registry.addInterceptor(roleInterceptor).addPathPatterns("/getstudent");
//registry.addInterceptor(new OtherInterceptor()).addPathPatterns("/**");
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
//第一个参数解析器
argumentResolvers.add(oenCurrentMethodArgumentResolver());
//第二个参数解析器
argumentResolvers.add(twoCurrentUserMethodArgumentResolver());
super.addArgumentResolvers(argumentResolvers);
}
@Bean//第一个参数解析器
public OenCurrentMethodArgumentResolver oenCurrentMethodArgumentResolver() {
return new OenCurrentMethodArgumentResolver();
}
@Bean//第二个参数解析器
public TwoCurrentMethodArgumentResolver twoCurrentUserMethodArgumentResolver() {
return new TwoCurrentMethodArgumentResolver();
}
/* @Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("/static/");
super.addResourceHandlers(registry);
}*/
}
6.在controller层中的使用
@RequestMapping("/getssl/{serialNumber}") @ResponseBody //@Pathvariable注解绑定它传过来的值到方法的参数上//RESTful 风格//http://10.20.10.86:9080/ssl/getssl/P100AFH00W public Ret<Map> getSSL(@PathVariable("serialNumber") String serialNumber) throws Exception {}
@OenIntercept @RequestMapping("/test") String test(@OenCurrent SMKeyBeen sMKeyBeen) throws Exception { SMKeyBeen niuhao = (SMKeyBeen) redisUtils.get("niuhao"); System.out.println(niuhao.getAddtime()); System.out.println(redisUtils.get("niuhao11")); //System.out.println("---0-000-"+sMKeyBeen.getId()); System.out.println("---0-000-" + sMKeyBeen.getId()); return "111111111"; } @TwoIntercept @RequestMapping("/tWO") @ResponseBody public String tWO(@TwoCurrent SMKeyBeen sMKeyBeen) throws Exception { // SMKeyBeen niuhao = crudRepository.findOne("123456"); // System.out.println(niuhao.getAddtime()); //System.out.println("---0-000-"+sMKeyBeen.getId()); System.out.println("---0-000-" + sMKeyBeen.getId()); return "111122"; }
请求getssl方式时,因为没有拦截器注解所以会执行
--进入拦截器AuthInterceptor--
--进入拦截器,类和方法上都没有拦截注解
也就是说在拦截器AuthInterceptor中执行了
if (!handlerMethod.hasMethodAnnotation(OenIntercept.class) && handlerMethod.getBeanType().getAnnotation(OenIntercept.class) == null) == null) {
System.out.println("--进入拦截器,类和方法上都没有拦截注解");
return true;
}
请求test方式时,因为该方法上有@OenIntercept的注解,同时有OenCurrent 用于标识用户实体类入参,参数级注解
所以会先进入AuthInterceptor中执行
if (method.isAnnotationPresent(OenIntercept.class) || handlerMethod.hasMethodAnnotation(OenIntercept.class)) {
log.info("--1111444IgnoreSecurity");
String token = request.getHeader("ACCESS_TOKEN");
token = "P100AFH00W";
log.debug("token: " + token);
if (token.equals("") || token == null) {
throw new Exception("无效token");
}
SMKeyBeen ssLs = sSLService.getSSLs(token);
System.out.println(ssLs.getId());
request.setAttribute("currentUser", ssLs);
return true;
}
然后进入执行OenCurrentMethodArgumentResolver,test方法就可接收到实体参数sMKeyBeen(原本请求路径为http://10.20.10.86:9080/ssl/test)并没有参数传递,同过拦截器实现了修改请求的参数。
7.多个拦截器注解和参数解析器。在代码中标注第一个xxx的地方复制粘贴即可,将Application.java中相关代码改为如下
/**
注册拦截器和参数解析器在 WebMvcConfigurerAdapter 中,需要注意,拦截器中引用了 UserService 所以在注册时需要使用 @Bean 的形式以告诉 Spring 注入
*/
@Configuration
public class MyWebMvcConfigurerAdapter extends WebMvcConfigurerAdapter {
//关键,将拦截器作为bean写入配置中
@Bean
public AuthInterceptor myAuthInterceptor() {
return new AuthInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册拦截器
InterceptorRegistration ir = registry.addInterceptor(myAuthInterceptor());
// 配置拦截的路径
ir.addPathPatterns("/**");
// 配置不拦截的路径
// ir.excludePathPatterns("/ssl/getssl");
// 还可以在这里注册其它的拦截器
//registry.addInterceptor(roleInterceptor).addPathPatterns("/getstudent");
//registry.addInterceptor(new OtherInterceptor()).addPathPatterns("/**");
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
//第一个参数解析器
argumentResolvers.add(oenCurrentMethodArgumentResolver());
//第二个参数解析器
argumentResolvers.add(twoCurrentUserMethodArgumentResolver());
super.addArgumentResolvers(argumentResolvers);
}
@Bean//第一个参数解析器
public OenCurrentMethodArgumentResolver oenCurrentMethodArgumentResolver() {
return new OenCurrentMethodArgumentResolver();
}
@Bean//第二个参数解析器
public TwoCurrentMethodArgumentResolver twoCurrentUserMethodArgumentResolver() {
return new TwoCurrentMethodArgumentResolver();
}
}
8.项目完整结构