拦截器使用总结
过滤器实现
1.拦截器中 获取body里面的内容后,controller获取body的值将会为空 会抛异常
@Order(1001)
//集成HandlerInterceptorAdapter实现
public class TokenInterceptors extends HandlerInterceptorAdapter {
private Logger logger = LoggerFactory.getLogger(TokenInterceptors.class);
//过滤器参数可以由构造器 外部传进来
public String screct;
public TokenInterceptors(String screct) {
this.screct = screct;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String body = new String();
//此处获取body使用Filter修饰过的 直接使用request.getInputStream()获取数据的话,controller将获取不到数据
if(request instanceof BodyReaderHttpServletRequestWrapper){
body = ((BodyReaderHttpServletRequestWrapper) request).getBodyStr();
}
String url = request.getRequestURI();
String method = request.getMethod();
String tokenReq = request.getParameter("token");
BaseResult baseResult = new BaseResult();
baseResult.setCode("-1");
if (!method.equals("POST")){
logger.error("request method is not post");
baseResult.setMsg("request method is not post");
returnJson(response, JsonUtil.toJSONNoFeatures(baseResult));
return false;
}
String token = TokenUtil.getToken(url, body, screct);
if (token.equals(tokenReq)) {
return super.preHandle(request, response, handler);
}
else {
logger.error("request token is error ");
baseResult.setMsg("request token is error");
returnJson(response, JsonUtil.toJSONNoFeatures(baseResult));
return false;
}
}
/**
* 获取POST请求中Body参数----------------这个方法使用request.getInputStream了 controller将获取不到body
* @param request
* @return 字符串
*/
public String getParm(HttpServletRequest request) {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(request.getInputStream(), "UTF-8"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String line = null;
StringBuilder sb = new StringBuilder();
try {
while ((line = br.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return sb.toString();
}
private void returnJson(HttpServletResponse response, String json) throws Exception {
PrintWriter writer = null;
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html; charset=utf-8");
try {
writer = response.getWriter();
writer.print(json);
} catch (IOException e) {
logger.error("response error", e);
} finally {
if (writer != null)
writer.close();
}
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
}
@Override
public void afterConcurrentHandlingStarted(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
}
}
拦截器配置
2.继承WebMvcConfigurationSupport(springboot2.x后用此类) WebMvcConfigurerAdapter
1.配置拦截地址和不拦截地址
3.addResourceHandlers增加获取本地文件 或者不拦截的url handle 比如swagger,当然地址可以在拦截器的不拦截地址里面配置 效果一样。
@Configuration
@Order(1001)
public class TokenConfig extends WebMvcConfigurerAdapter {
@Value("${sddi.custom.api.screct}")
public String screct;
/**
*拦截器排除的URL
*/
@Value("${token.interceptor.excludePathPatterns:}")
private String excludePathPatterns;
/**
*拦截URL
*/
@Value("/**")
private String pathPatterns;
// 这个方法用来注册拦截器,我们自己写好的拦截器需要通过这里添加注册才能生效
@Override
public void addInterceptors(InterceptorRegistry registry) {
InterceptorRegistration interceptorRegistration = registry.addInterceptor(
new TokenInterceptors(screct));
String[] paths = this.pathPatterns.split(",");
for(int i = 0; i < paths.length; ++i) {
interceptorRegistration.addPathPatterns(new String[]{paths[i]});
}
//不拦截的地址
if (!StringUtils.isEmpty(this.excludePathPatterns)) {
String[] excludePaths = this.excludePathPatterns.split(",");
for(int i = 0; i < excludePaths.length; ++i) {
interceptorRegistration.excludePathPatterns(new String[]{excludePaths[i]});
}
}
super.addInterceptors(registry);
}
// 这个方法是用来配置静态资源的,比如html,js,css,等等
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
4.注意点,当拦截器通过后,如果调用controller方法出现异常,这个时候如果没有 异常处理ControllerAdvice,会进入到SpringBoot默认全局异常,也就是我们的/error请求,最尴尬的是,这个请求如果没有在拦截器中配置/**/error的不过滤,这个请求也会进入到拦截器,这个时候 就会进入拦截器拦截逻辑,返回的就是是拦截器错误给到前端。
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
private final ErrorProperties errorProperties;
..........
解决方法就是写一个异常拦截器@RestControllerAdvice的实现。
@RestControllerAdvice(basePackages = "com.hikvision.sddicustom.business.controller")
public class ExceptionHandlerAdvice {
private static final HikGaLogger LOGGER = HikGaLoggerFactory.getLogger(ExceptionHandlerAdvice.class);
/**
* 参数校验异常 validate框架
* 对象参数接收请求体: MethodArgumentNotValidException
* 请求参数绑定到对象参数上: BindException
* 普通参数: ConstraintViolationException
* 必填参数没传: ServletRequestBindingException
* 必填请求参数缺失:MissingServletRequestParameterException
* 路径参数缺失:MissingPathVariableException
* @param ex
* @return
*/
@ExceptionHandler(value = {ValidationException.class, ConstraintViolationException.class,
MethodArgumentNotValidException.class, ServletRequestBindingException.class, BindException.class,
MissingPathVariableException.class, MissingServletRequestParameterException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
public BaseResult handleBadRequestException(Exception ex) {
String errorCode = ErrorCode.PARAMETER_VALIDATE_EXCEPTION.getCode();
String msg = "";
//validate参数校验异常获取具体描述信息
if (ex instanceof ValidationException) {
ValidationException validationException = (ValidationException)ex;
if (!StringUtils.isEmpty(validationException.getMessage())) {
String validteMsg = validationException.getMessage();
if(!StringUtils.isEmpty(validteMsg) && validteMsg.indexOf(":") > -1){
String[] msgArr = validteMsg.split(":");
if(!StringUtils.isEmpty(msgArr[1])){
msg = msgArr[1];
}
}
}
}
LOGGER.errorWithErrorCode(errorCode,msg,ex);
return BaseResult.error(errorCode,msg);
}
/**
* 未知异常处理
* @param ex
* @return
*/
@ExceptionHandler(value = {Exception.class})
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public BaseResult handleRunTimeException(Exception ex) {
LOGGER.errorWithErrorCode(ErrorCode.UNKNOWN_EXCEPTION.getCode(),ErrorCode.UNKNOWN_EXCEPTION.getChinaMessage(),ex);
return BaseResult.fail(ErrorCode.UNKNOWN_EXCEPTION.getCode(),ErrorCode.UNKNOWN_EXCEPTION.getChinaMessage());
}
}
下面给出获取body的方法 —需要使用过滤器fillter ----这里不是为了介绍Filter的使用,这里的filter是为了配合拦截器使用。
1.封装HttpServletRequestWrapper获取body
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
private String bodyStr;
public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
String bodyString = getBodyString(request);
body = bodyString.getBytes(Charset.forName("UTF-8"));
bodyStr=bodyString;
}
public String getBodyStr() {
return bodyStr;
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
public String getBodyString(HttpServletRequest request) throws IOException {
StringBuilder sb = new StringBuilder();
InputStream inputStream = null;
BufferedReader reader = null;
try {
inputStream = request.getInputStream();
reader = new BufferedReader(
new InputStreamReader(inputStream, Charset.forName("UTF-8")));
char[] bodyCharBuffer = new char[1024];
int len = 0;
while ((len = reader.read(bodyCharBuffer)) != -1) {
sb.append(new String(bodyCharBuffer, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return sb.toString();
}
}
2.使用Filter进行 request转换,加上WebFilter注解 还需要使用Component 或者@ServletComponentScan来让Filter起作用(配置Filter的方式很多 这种偷懒的方式)
@WebFilter(filterName = "httpServletRequestWrapperFilter", urlPatterns = {"/*"})
@Component
public class HttpServletRequestWrapperFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
ServletRequest requestWrapper = null;
if (request instanceof HttpServletRequest) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
//遇到post方法才对request进行包装
String methodType = httpRequest.getMethod();
if ("POST".equals(methodType)) {
requestWrapper = new BodyReaderHttpServletRequestWrapper(
(HttpServletRequest) request);
}
}
if (null == requestWrapper) {
chain.doFilter(request, response);
} else {
chain.doFilter(requestWrapper, response);
}
}
@Override
public void destroy() {
}
}
过滤器的添加方式 这边 顺带提一下,以后再补充吧
第一种在Application中加一次 继承ServletContextInitializer
public class RpidWebApplicatin implements ServletContextInitializer {
public static void main(String[] args) {
SpringApplication.run(RpidWebApplicatin.class, args);
}
@Override
public void onStartup(ServletContext sc) throws ServletException {
sc.addListener(new SingleSignOutHttpSessionListener());
FilterRegistration.Dynamic cASLogoutFilter = sc.addFilter("CASLogoutFilter", SingleSignOutFilter.class);
cASLogoutFilter.addMappingForUrlPatterns(null, false, "/*");
FilterRegistration.Dynamic cASAuthenticationFilter = sc.addFilter("CAS Authentication Filter",
HikAuthenticationFilter.class);
cASAuthenticationFilter.addMappingForUrlPatterns(null, false, "/*");
FilterRegistration.Dynamic cas20Registration = sc.addFilter("CAS Validation Filter",
HikCas20ProxyReceivingTicketValidationFilter.class);
cas20Registration.setInitParameter("encoding", "UTF-8");
cas20Registration.addMappingForUrlPatterns(null, false, "/*");
}
}
第二种 使用FilterRegistrationBean
@Bean
public FilterRegistrationBean myFilter(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new MyFilter());
registrationBean.setUrlPatterns(Arrays.asList("/hello","/myServlet"));
return registrationBean;
}
实际看源码会发现
ServletContextInitializer->RegistrationBean->FilterRegistrationBean
ServletContextInitializer是最基本的类 FilterRegistrationBean只是对过滤器的继承实现
实际servlet 和 listener filter都是有RegistrationBean的
最终看自己的喜欢去使用过滤器的使用了
其实自己搞了一个下午 最终发现上面的功能使用 切面去实现 是最方便的 使用拦截器有点复杂 不过多学习了一点知识 也是很好的 希望上面的坑大家遇到了 能对大家有好处。