攔截器使用總結
過濾器實現
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的
最終看自己的喜歡去使用過濾器的使用了
其實自己搞了一個下午 最終發現上面的功能使用 切面去實現 是最方便的 使用攔截器有點複雜 不過多學習了一點知識 也是很好的 希望上面的坑大家遇到了 能對大家有好處。