zoukankan      html  css  js  c++  java
  • SpringBoot2.X自定义参数拦截器,同一请求被拦截两次处理方法, redis在拦截器中无法加载的问题

    场景:

      java和php两个平台的登录模块不同,现在php需要调用java的一个接口,目前想到是通过redis中的token校验,有好的方法请大神指点。

      想自定义一个拦截器,因为需要redis中的token数据,所有需要在拦截器中注入redis工具类,就用了构造方法加载;

      之后发现每次请求都会被拦截两次,然后再拦截器中打印request.getRequestURI() 发现第一次是请求地址,第二次是/error地址,所以在代码中添加excludePathPatterns("/error");

      总感觉此方法不是很好,如有不对请指正。

    1、自定义拦截器

    import cn.fookey.payment.utils.RedisOperator;
    import com.alibaba.fastjson.JSONObject;
    import java.io.PrintWriter;
    import java.util.Map;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.HandlerInterceptor;
    
    /**
     *
     * http请求拦截器
     * @Author TCL
     * @Date 2021/4/26 16:53
     * @Version 1.0
     */
    @Component
    public class RequestInterceptor implements HandlerInterceptor {
    
    
        private RedisOperator redisOperator;
       
      //拦截器优先bean的加载,所以使用构造方法
        public RequestInterceptor (RedisOperator redisOperator) {
            this.redisOperator = redisOperator;
        }
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws Exception {
            //获取请求body  
            //RequestParamsWrapper类 解决request请求流只能读取一次的问题
            String bodyString = new RequestParamsWrapper(request).getBodyString();
            Map<String,Object> map = JSONObject.parseObject(bodyString, Map.class);
            // 获取redis中token
            String redisToken =  redisOperator.get(map.get("accountId"));
           
            // 校验token
            if (!map.get("token").equals(redisToken)) {
                response.setCharacterEncoding("UTF-8");
                response.setContentType("application/json;charset=UTF-8");
                PrintWriter writer = response.getWriter();
                writer.print("{"status": 500,"msg": "用户令牌失效!","data": null}");
                return false;
            }
            return true;
        }
    }

    2、拦截器中,request中参数只能调用一次,因为是自定义参数,在拦截器中使用参数以后到controller中就获取不到了,所有添加一个请求参数包装器。

    import java.io.BufferedReader;
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.nio.charset.Charset;
    import javax.servlet.ReadListener;
    import javax.servlet.ServletInputStream;
    import javax.servlet.ServletRequest;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * 请求参数包装器
     * springboot拦截器校验或鉴权导致Required request body is missing解决方法
     * 解决思路:重新定义一个HttpServletRequestWrapper来代替request,里面的流复制保存,就不会丢失
     * @Author TCL
     * @Date 2021/4/26 9:57
     * @Version 1.0
     */
    @Slf4j
    public class RequestParamsWrapper extends HttpServletRequestWrapper {
    
        /**
         * 存储body数据的容器
         */
        private final byte[] body;
    
        public RequestParamsWrapper(HttpServletRequest request) throws IOException {
            super(request);
    
            // 将body数据存储起来
            String bodyStr = getBodyString(request);
            body = bodyStr.getBytes(Charset.defaultCharset());
        }
    
        /**
         * 获取请求Body
         *
         * @param request request
         * @return String
         */
        public String getBodyString(final ServletRequest request) {
            try {
                return inputStream2String(request.getInputStream());
            } catch (IOException e) {
                log.error("", e);
                throw new RuntimeException(e);
            }
        }
    
        /**
         * 获取请求Body
         *
         * @return String
         */
        public String getBodyString() {
            final InputStream inputStream = new ByteArrayInputStream(body);
    
            return inputStream2String(inputStream);
        }
    
        /**
         * 将inputStream里的数据读取出来并转换成字符串
         *
         * @param inputStream inputStream
         * @return String
         */
        private String inputStream2String(InputStream inputStream) {
            StringBuilder sb = new StringBuilder();
            BufferedReader reader = null;
    
            try {
                reader = new BufferedReader(new InputStreamReader(inputStream, Charset.defaultCharset()));
                String line;
                while ((line = reader.readLine()) != null) {
                    sb.append(line);
                }
            } catch (IOException e) {
                log.error("", e);
                throw new RuntimeException(e);
            } finally {
                if (reader != null) {
                    try {
                        reader.close();
                    } catch (IOException e) {
                        log.error("", e);
                    }
                }
            }
    
            return sb.toString();
        }
    
        @Override
        public BufferedReader getReader() throws IOException {
            return new BufferedReader(new InputStreamReader(getInputStream()));
        }
    
        @Override
        public ServletInputStream getInputStream() throws IOException {
    
            final ByteArrayInputStream inputStream = new ByteArrayInputStream(body);
    
            return new ServletInputStream() {
                @Override
                public int read() throws IOException {
                    return inputStream.read();
                }
    
                @Override
                public boolean isFinished() {
                    return false;
                }
    
                @Override
                public boolean isReady() {
                    return false;
                }
    
                @Override
                public void setReadListener(ReadListener readListener) {
                }
            };
        }
    }

    3、配置WebMvcConfigurer

    import cn.fookey.payment.utils.RedisOperator;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    /**
     * 
     * @Author TCL
     * @Date 2021/4/26 17:11
     * @Version 1.0
     */
    
    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
        @Autowired
        private RedisOperator redisOperator;
    
        //过滤器
        @Bean
        public FilterRegistrationBean<RequestParamsFilter> Filters() {
            FilterRegistrationBean<RequestParamsFilter> registrationBean = new FilterRegistrationBean<>();
            registrationBean.setFilter(new RequestParamsFilter());
            registrationBean.addUrlPatterns("/*");
            return registrationBean;
        }
    
        //自定义拦截器
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new RequestInterceptor(redisOperator))
                    .excludePathPatterns("classpath:/META-INF/resources/")
                    //同一请求被拦截两次处理方法
                    .excludePathPatterns("/error")
                    .addPathPatterns("/**");
        }
    }

    4、重写过滤器中的方法

    import java.io.IOException;
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletRequest;
    
    /**
     * 
     * @Author TCL
     * @Date 2021/4/26 15:30
     * @Version 1.0
     */
    @WebFilter(filterName="bodyReaderFilter",urlPatterns="/*")
    public class RequestParamsFilter implements Filter {
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            // do nothing
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
            ServletRequest requestWrapper=null;
            if(request instanceof HttpServletRequest) {
                requestWrapper=new RequestParamsWrapper((HttpServletRequest)request);
            }
            if(requestWrapper==null) {
                chain.doFilter(request, response);
            }else {
                chain.doFilter(requestWrapper, response);
            }
    
        }
    
        @Override
        public void destroy() {
            // do nothing
    
        }
    }

    以上哪里有不对,请指正,谢谢。

  • 相关阅读:
    请简单介绍spring支持的常用数据库事务传播属性和事务隔离级别
    Spring Bean的作用域
    成员变量与局部变量的区别
    递归与迭代
    方法参数的传递机制 ---- 值传递
    windows phone 动画 当子控件超出父控件返回时
    把dataset 输出到 excel
    代码段
    c++ 学习纪录
    做自己的代码生成器
  • 原文地址:https://www.cnblogs.com/guduershi/p/14713327.html
Copyright © 2011-2022 走看看