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處理完成返回後,攔截器還可以做其他過程(還沒想到要做啥),再向上返回到過濾器的後續操作。
那麼我們開始實現吧:
- 實現自己的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(); }
- 實現Filter介面,攔截器:過濾器,是在java web中,你傳入的request,response提前過濾掉一些資訊,或者提前設定一些引數,然後再傳入servlet或者struts的 action進行業務邏輯,比如過濾掉非法url(不是login.do的地址請求,如果使用者沒有登陸都過濾掉),或者在傳入servlet或者 struts的action前統一設定字符集,或者去除掉一些非法字元
-
@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); }
這樣就可以順利的執行啦,在一開始的時候我沒有實現過濾器,所以還是獲取不到,所以這三個都是必須實現的