1. 程式人生 > >spring 框架下,如何通過攔截器和過濾器讀取request裡的內容

spring 框架下,如何通過攔截器和過濾器讀取request裡的內容

當我們用spring 攔截器實現許可權校驗的時候,往往我們需要從request中獲取一些資料進行校驗,但是當我們在攔截器獲取到資料,getinputStream,那麼在後續的action即:controller中我們獲取不到request,這是為什麼呢?因為java.util.Map所包裝的HttpServletRequest物件的引數是不可改變的。 這句話的意思就是我們通過request.getInputStream.那麼request就沒有啦。那麼我們後續的操作也就拿不到request啦。這就影響我們後續的操作。那麼既然我們知道問題所在,我們只需要把request拿出來,複製一份,儲存,當我們攔截器完成工作以後,我們將我們儲存的request輸入進去,就可以進行後續的操作。如果我們想要塞進去,那麼我們就要實現一個過濾器,攔截器,還有HttpServletRequestWrapper包裝器。攔截器與過濾器的區別:攔截器是基於java的反射機制的,而過濾器是基於函式回撥。攔截器不依賴與servlet容器,過濾器依賴與servlet容器。攔截器只能對action請求起作用,而過濾器則可以對幾乎所有的請求起作用。攔截器可以訪問action上下文、值棧裡的物件,而過濾器不能訪問。在action的生命週期中,攔截器可以多次被呼叫,而過濾器只能在容器初始化時被呼叫一次。執行順序:過濾前 – 攔截前 – Action處理 – 攔截後 – 過濾後。個人認為過濾是一個橫向的過程,首先把客戶端提交的內容進行過濾(例如未登入使用者不能訪問內部頁面的處理);過濾通過後,攔截器將檢查使用者提交資料的驗證,做一些前期的資料處理,接著把處理後的資料發給對應的Action;Action處理完成返回後,攔截器還可以做其他過程(還沒想到要做啥),再向上返回到過濾器的後續操作。

那麼我們開始實現吧:

  1. 實現自己的HttpServletRequestWrapper 類,將request儲存到wrapper的body欄位中。
    
    	private final static Logger log = LoggerFactory.getLogger(RequestToJson.class);
    	private final byte[] body;
    
    	public RequestToJson(HttpServletRequest request) throws IOException {
    		super(request);
    		String sessionStream = getBodyString(request);
    		log.info("RequestToJson body:"+sessionStream);
    		body = sessionStream.getBytes(Charset.forName("UTF-8"));
    	}
    
    	
    	@Override
    	public ServletInputStream getInputStream() throws IOException {
            final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
            ServletInputStream servletInputStream = new ServletInputStream() {
                public int read() throws IOException {
                    return byteArrayInputStream.read();
                }
    
    			@Override
    			public boolean isFinished() {
    				// TODO Auto-generated method stub
    				return false;
    			}
    
    			@Override
    			public boolean isReady() {
    				// TODO Auto-generated method stub
    				return false;
    			}
    
    			@Override
    			public void setReadListener(ReadListener listener) {
    				// TODO Auto-generated method stub
    				
    			}
            };
            return servletInputStream;
        }
    //返回自己的inputStream
    	@Override
    	public BufferedReader getReader() throws IOException {
    		return new BufferedReader(new InputStreamReader(this.getInputStream()));
    	}
    
    	/**
    	 * 複製input輸入流
    	 * 
    	 * @param inputStream
    	 * @return
    	 * @throws IOException
    	 */
    	public InputStream cloneInputStream(ServletInputStream inputStream) throws IOException {
    		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    		byte[] buffer = new byte[1024];
    		int len = 0;
    		try {
    			while ((len = inputStream.read(buffer)) > -1) {
    				byteArrayOutputStream.write(buffer, 0, len);
    			}
    			byteArrayOutputStream.flush();
    		} catch (IOException e) {
    			log.info("clone servletInputStream failed", e);
    			throw e;
    		}
    		InputStream byInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
    		return byInputStream;
    	}
    //獲取request中的getInputStream,返回String欄位
    	public String getBodyString(final ServletRequest request) {
    		StringBuilder sb = new StringBuilder();
    		InputStream inputStream = null;
    		BufferedReader reader = null;
    		try {
    			inputStream = cloneInputStream(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) {
    			log.info("get request body code error:", e);
    		} finally {
    			if (inputStream != null) {
    				try {
    					inputStream.close();
    				} catch (IOException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    			}
    			if (reader != null) {
    				try {
    					reader.close();
    				} catch (IOException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    			}
    		}
    		return sb.toString();
    	}
    
  2. 實現Filter介面,攔截器:過濾器,是在java web中,你傳入的request,response提前過濾掉一些資訊,或者提前設定一些引數,然後再傳入servlet或者struts的 action進行業務邏輯,比如過濾掉非法url(不是login.do的地址請求,如果使用者沒有登陸都過濾掉),或者在傳入servlet或者 struts的action前統一設定字符集,或者去除掉一些非法字元 
  3. @Component
    @WebFilter(filterName = "NowidUserFilter", urlPatterns = "/user/*")
    public class NowidUserFilter implements Filter {
    	private final static Logger log = LoggerFactory.getLogger(NowidUserFilter.class);
    
    	@Override
    	public void init(FilterConfig filterConfig) throws ServletException {
    		// TODO Auto-generated method stub
    
    	}
    
    	@Override
    	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    			throws IOException, ServletException {
    		log.info("execute NowidUserFilter 。。。。");
    		HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            //儲存到自我實現的包裝器,body是不可改變
    		ServletRequest requestWrapper = new RequestToJson(httpServletRequest);
    		chain.doFilter(requestWrapper, response); 
    
    	}
    
    	@Override
    	public void destroy() {
    		// TODO Auto-generated method stub
    
    	}
    
    }
    

      實現攔截器,然後呼叫包裝類中的方法返回字串,就可以自我操作啦。我的實現是json,用的是阿里巴巴的fastjson

    @Component
    public class NowIdUserStatusInterceptor implements HandlerInterceptor {
    	private static Logger logger = LoggerFactory.getLogger(NowIdUserStatusInterceptor.class);
    	@Autowired
    	private RedisTemplate<String, String> redisTemplate;
    
    	@Override
    	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
    			throws Exception {
    		logger.info("NowIdUserStatusInterceptor>>>>preHandle");
    		String ip = Tool.getRemoteIp(request);
    		RequestToJson reqJson = new RequestToJson(request);
    		JSONObject jsonObject = JSONObject.parseObject(reqJson.getBodyString(request));
    		String phone = jsonObject.getString("phone");
    		String userId = jsonObject.getString("userid");
    		String redisuserid = redisTemplate.opsForValue().get("user"+ip+userId);
    		String redisphone = redisTemplate.opsForValue().get("user" + ip + phone);
    		logger.info("login user is :"+redisuserid +" |"+redisphone);
    		if (redisuserid == null && redisphone == null) {
    			// 獲取專案路徑+登入路徑
    			response.sendRedirect(request.getContextPath() + "/admin/login");
    			return false;
    		} else {
    //			logger.info("攔截器校驗通過" +request);
    			return true;
    		}
    	}
    
    	@Override
    	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
    			ModelAndView modelAndView) throws Exception {
    		// TODO Auto-generated method stub
    		HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    	}
    
    	@Override
    	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
    			throws Exception {
    		// TODO Auto-generated method stub
    		HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    	}
    

    這樣就可以順利的執行啦,在一開始的時候我沒有實現過濾器,所以還是獲取不到,所以這三個都是必須實現的