攔截器使用總結

攔截器使用總結

過濾器實現

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的

最終看自己的喜歡去使用過濾器的使用了

其實自己搞了一個下午 最終發現上面的功能使用 切面去實現 是最方便的 使用攔截器有點複雜 不過多學習了一點知識 也是很好的 希望上面的坑大家遇到了 能對大家有好處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章