• Java学习-5 折腾了几晚的Filter、HandlerInterceptorAdapter、RequestWrapper


    直接上代码吧,懒得写了

    1、过滤器,难点:

    service的调用,网上说可以Autowired,我试了半天不行,最后在init里去手动是可以了,而且观察也只会初始化一次,一样的。

    body参数的获取后,流会关闭。如果不考虑其它地方还要再取值,可用ContentCachingRequestWrapper。

    如果在拦截器等地方还要再取的话,要用RequestWrapper,代码在后面

    package com.filter;
    
    import com.model.user.User;
    import com.service.sys.LogService;
    import com.util.StringUtils;
    import org.springframework.web.util.ContentCachingRequestWrapper;
    import org.springframework.web.util.ContentCachingResponseWrapper;
    
    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.Date;
    
    public class LoginFilter implements Filter {
        //无法用AutoWired自动注入,改为在init里手动注入
        private LogService logService;
    
        @Override
        public void init(FilterConfig filterConfig) {
            /*
            //试了半天DelegatingFilterProxy,一直不行,还是手动注入可以就算了,跟踪发现只会调用一次,也一样
            if (logService == null) {
                ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(filterConfig.getServletContext());
                LogService logService = context.getBean(LogService.class);
                this.logService = logService;
            }
            */
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            //改在拦截器里处理,此处直接放过
    //        chain.doFilter(request, response);
    
            RequestWrapper requestWrapper = null;
            if (request instanceof HttpServletRequest) {
                requestWrapper = new RequestWrapper((HttpServletRequest) request);
            }
            if (requestWrapper == null) {
                chain.doFilter(request, response);
            } else {
                chain.doFilter(requestWrapper, response);
            }
    
    
            /*
            HttpServletRequest servletRequest = (HttpServletRequest) request;
            HttpServletResponse servletResponse = (HttpServletResponse) response;
            HttpSession session = servletRequest.getSession();
    
            // 获得用户请求的URI
            String path = servletRequest.getRequestURI();
            User user = (User) session.getAttribute("user");
    
            // 登陆页面无需过滤
            if (path.contains("/login/")) {
                doFilter(servletRequest, servletResponse, chain, user);
                return;
            }
            // 判断如果没有取到账号信息,就跳转到登陆页面
            if (user == null) {
                // 跳转到登陆页面
                servletResponse.sendRedirect(servletRequest.getContextPath() + "/login/index.do");
            } else {
                // 已经登陆,继续此次请求。如有需要,可以再验证下账号的操作权限,以防跳过前端界面,直接用工具后台发起的请求
                doFilter(servletRequest, servletResponse, chain, user);
            }
            */
        }
    
        void doFilter(HttpServletRequest servletRequest, HttpServletResponse servletResponse, FilterChain chain, User user) throws IOException, ServletException {
            String contentType = servletRequest.getContentType();
            String method = servletRequest.getMethod();
            String path = servletRequest.getServletPath();
    
            if (contentType != null && contentType.startsWith("multipart/form-data") && "POST".equalsIgnoreCase(method)) {
                System.out.println("当前请求为文件上传,不作请求日志收集");
            } else {
                ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(servletRequest);
                ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(servletResponse);
                long start = new Date().getTime();
                try {
                    chain.doFilter(requestWrapper, responseWrapper);
                } finally {
                    String requestBody = servletRequest.getQueryString();
                    if (StringUtils.isEmpty(requestBody)) {
                        requestBody = new String(requestWrapper.getContentAsByteArray());
                    }
                    responseWrapper.copyBodyToResponse();
                }
            }
        }
    
        @Override
        public void destroy() {
    
        }
    }

    2、RequestWrapper

    网上最多的就是这个,但我试了半天,一直踩坑。主要有:

    import javax.servlet.ReadListener;  这个找不到,后来发现是servlet版本太低,升高版本就有了

    照着抄后,post提交的body仍然消失了(但没报错),表现在比如登录,明明输了账号密码,也能在Filter里接收到参数,但Controller就是为空,坑了几晚

    package com.filter;
    
    import com.util.HttpUtil;
    
    import javax.servlet.ReadListener;
    import javax.servlet.ServletInputStream;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    import java.io.BufferedReader;
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.nio.charset.Charset;
    import java.util.Enumeration;
    
    public class RequestWrapper extends HttpServletRequestWrapper {
        private final byte[] body;
    
        public RequestWrapper(HttpServletRequest request) {
            super(request);
            body = HttpUtil.getBodyString(request).getBytes(Charset.forName("UTF-8"));
        }
    
        @Override
        public BufferedReader getReader() throws IOException {
            return new BufferedReader(new InputStreamReader(getInputStream()));
        }
    
        @Override
        public ServletInputStream getInputStream() {
            final ByteArrayInputStream bais = new ByteArrayInputStream(body);
    
            return new ServletInputStream() {
                @Override
                public boolean isFinished() {
                    return false;
                }
    
                @Override
                public boolean isReady() {
                    return false;
                }
    
                @Override
                public void setReadListener(ReadListener readListener) {
                }
    
                @Override
                public int read() {
                    return bais.read();
                }
            };
        }
    
        @Override
        public String getHeader(String name) {
            return super.getHeader(name);
        }
    
        @Override
        public Enumeration<String> getHeaderNames() {
            return super.getHeaderNames();
        }
    
        @Override
        public Enumeration<String> getHeaders(String name) {
            return super.getHeaders(name);
        }
    }

    3、关键的就在这里,最后还是一篇贴子救了命

    http://blog.sina.com.cn/s/blog_550048a30102x7pp.html

    在大神的代码下,终于可以了,泪流满面!

    其实之前两种代码风格都有见过,就是不知道要判断一下,按contenttype来区分

    public static String getBodyString(ServletRequest request) {
            String contenttype = request.getContentType();
            if (contenttype != null && contenttype.contains("x-www-form-urlencoded")) {
                String bodystring = "";
                Enumeration pars = request.getParameterNames();
                while (pars.hasMoreElements()) {
                    String n = (String) pars.nextElement();
                    bodystring += n + "=" + request.getParameter(n) + "&";
                }
                bodystring = bodystring.endsWith("&") ? bodystring.substring(0, bodystring.length() - 1) : bodystring;
                return bodystring;
    
            } else if (contenttype != null && contenttype.contains("json")) {
                StringBuilder sb = new StringBuilder();
                InputStream inputStream = null;
                BufferedReader reader = null;
                try {
                    inputStream = request.getInputStream();
                    reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
                    String line;
                    while ((line = reader.readLine()) != null) {
                        sb.append(line);
                    }
                } 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();
            }
            return "";
        }

    4、过滤器,如果前面没处理好,这边要么是获取到的post是空,要么一获取完post,就会导致Controller没值,或是报什么流关闭之类的,也是折腾了几晚

    package com.filter;
    
    import com.model.sys.Log;
    import com.model.user.User;
    import com.service.sys.LogService;
    import com.util.HttpUtil;
    import com.util.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.core.NamedThreadLocal;
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    
    public class AuthInterceptor extends HandlerInterceptorAdapter {
        @Autowired
        private LogService logService;
    
        private NamedThreadLocal<Long> startTimeThreadLocal = new NamedThreadLocal<>("StopWatch-StartTime");
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            String path = request.getRequestURI();
            HttpSession session = request.getSession();
            User user = (User) session.getAttribute("user");
            // 登陆页面无需过滤
            if (path.contains("/login/")) {
                return true;
            }
            // 判断如果没有取到账号信息,就跳转到登陆页面
            if (user == null) {
                // 跳转到登陆页面
                response.sendRedirect(request.getContextPath() + "/login/index.do"); //response是整个页面跳转
                //request.getRequestDispatcher("/login/index.do").forward(request, response); //request是内部重定向
                return true;
            } else {
                startTimeThreadLocal.set(System.currentTimeMillis());//线程安全(该数据只有当前请求的线程可见)
                return true;
            }
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws IOException {
            Object startTimeObj = startTimeThreadLocal.get();
            if (startTimeObj == null) return;
    
            long time = System.currentTimeMillis() - startTimeThreadLocal.get();
            HandlerMethod methodHandler = (HandlerMethod) handler;
            HttpSession session = request.getSession();
            String path = request.getServletPath();
            User user = (User) session.getAttribute("user");
            AuthAnnotation authAnnotation = methodHandler.getMethodAnnotation(AuthAnnotation.class);
            String description = authAnnotation != null ? authAnnotation.description() : null;
    
            String params = request.getQueryString();
            if (StringUtils.isEmpty(params)) {
                params = HttpUtil.getBodyString(request);
            }
    
            Log log = new Log();
            log.setCrudType("MVC");
            log.setUserId(user.getId());
            log.setUrl(path);
            log.setDescription(description);
            log.setRespTime(StringUtils.toInteger(time));
            log.setParams(params);
            logService.insert(log);
        }
    }

    5、本来在Filter里处理也是可以,但Filter处理不了控制器的属性(或是我不会),最终换到拦截器里处理,过滤器本来直接放空,但发现不行,就是上面说的流只能读一次的问题。现在过滤器就是中转一下,业务的验证逻辑还是放在拦截器里处理,就是为了这种控制器上面自定义的属性

      @AuthAnnotation(description = "业务流程", authType = AuthAnnotation.AuthType.LOGIN)
        @RequestMapping(value = "/wfFlow")
        public String wfFlow() {
            return "views/workflow/wfFlow";
        }
    package com.filter;
    
    import java.lang.annotation.*;
    
    @Documented //文档生成时,该注解将被包含在javadoc中,可去掉
    //@Target(ElementType.METHOD)//目标是方法
    @Retention(RetentionPolicy.RUNTIME) //注解会在class中存在,运行时可通过反射获取
    @Inherited
    public @interface AuthAnnotation {
        String description();
    
        enum AuthType {
            PUBLIC, LOGIN, EDIT
        }
    
        AuthType authType() default AuthType.LOGIN;
    }

    终于大功告成了,在此过程中,熟悉了过滤器和拦截器的各种坑,也算是有收获

  • 相关阅读:
    头信息已输出的报错信息位置定位
    阅读<php程序设计>笔记
    include、ruquire使用相对路径总结
    php中未定义的变量使用技巧
    Oracle官方教材(9i、10G及App 11i)
    Vista的软件兼容性
    轻松找回Vista序列号
    oralce定时执行存储过程任务设置步骤详细
    今天做了内存测试,发现真的是内存问题导致的一连串的问题
    网络邮盘(GMailStore) V3.0.2
  • 原文地址:https://www.cnblogs.com/liuyouying/p/11354863.html
Copyright © 2020-2023  润新知