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

    }
}

 

以上哪里有不對,請指正,謝謝。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM