摘要:前言在上篇博文中,我們已經(jīng)講解了過濾器的基本概念,使用以及簡單的應(yīng)用了。這篇博文主要講解過濾器的高級應(yīng)用。。編碼過濾器目的解決全站的亂碼問題開發(fā)過濾器將和強(qiáng)轉(zhuǎn)成協(xié)議的第一次測試中向?yàn)g覽器回應(yīng)中文數(shù)據(jù),沒有出現(xiàn)亂碼。
前言
在上篇博文中,我們已經(jīng)講解了過濾器的基本概念,使用以及簡單的Servlet應(yīng)用了。這篇博文主要講解過濾器的高級應(yīng)用。。編碼過濾器
目的:解決全站的亂碼問題
開發(fā)過濾器public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { //將request和response強(qiáng)轉(zhuǎn)成http協(xié)議的 HttpServletRequest httpServletRequest = (HttpServletRequest) req; HttpServletResponse httpServletResponse = (HttpServletResponse) resp; httpServletRequest.setCharacterEncoding("UTF-8"); httpServletResponse.setCharacterEncoding("UTF-8"); httpServletResponse.setContentType("text/html;charset=UTF-8"); chain.doFilter(httpServletRequest, httpServletResponse); }第一次測試
Servlet1中向?yàn)g覽器回應(yīng)中文數(shù)據(jù),沒有出現(xiàn)亂碼。
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().write("看完博客點(diǎn)贊!"); }分析
上面的過濾器是不完善的,因?yàn)?strong>瀏覽器用get方式提交給服務(wù)器的中文數(shù)據(jù),單單靠上面的過濾器是無法完成的!
那么我們需要怎么做呢??我們之前解決get方式的亂碼問題是這樣的:使用request獲取傳遞過來的數(shù)據(jù),經(jīng)過ISO 8859-1反編碼獲取得到不是亂碼的數(shù)據(jù)(傳到Servlet上的數(shù)據(jù)已經(jīng)被ISO 8859-1編碼過了,反編碼就可以獲取原來的數(shù)據(jù)),再用UTF-8編碼,得到中文數(shù)據(jù)!
參考我之前的博文:https://mp.weixin.qq.com/s?__biz=MzI4Njg5MDA5NA==&mid=100000001&idx=6&sn=b0c346d7f17a097525ad106291aad28c&chksm=6bd740005ca0c916f72872122dff3bad8c49f9f690ea51b442b542dbbb654382bb663a01473b&mpshare=1&scene=1&srcid=0206hvswcHK67to1b4tr46AK#rd
在Servlet獲取瀏覽器以GET方式提交過來的中文是亂碼的根本原因是:getParameter()方法是以ISO 8859-1的編碼來獲取瀏覽器傳遞過來的數(shù)據(jù)的,得到的是亂碼
既然知道了根本原因,那也好辦了:過濾器傳遞的request對象,使用getParameter()方法的時候,獲取得到的是正常的中文數(shù)據(jù)
也就是說,sun公司為我們提供的request對象是不夠用的,因?yàn)閟un公司提供的request對象使用getParameter()獲取get方式提交過來的數(shù)據(jù)是亂碼,于是我們要增強(qiáng)request對象(使得getParameter()獲取得到的是中文)!
增強(qiáng)request對象增強(qiáng)request對象,我們要使用包裝設(shè)計(jì)模式!
包裝設(shè)計(jì)模式的五個步驟:
1、實(shí)現(xiàn)與被增強(qiáng)對象相同的接口
2、定義一個變量記住被增強(qiáng)對象
3、定義一個構(gòu)造器,接收被增強(qiáng)對象
4、覆蓋需要增強(qiáng)的方法
5、對于不想增強(qiáng)的方法,直接調(diào)用被增強(qiáng)對象(目標(biāo)對象)的方法
sun公司也知道我們可能對request對象的方法不滿意,于是提供了HttpServletRequestWrapper類給我們實(shí)現(xiàn)(如果實(shí)現(xiàn)HttpServletRequest接口的話,要實(shí)現(xiàn)太多的方法了?。?/strong>
class MyRequest extends HttpServletRequestWrapper { private HttpServletRequest request; public MyRequest(HttpServletRequest request) { super(request); this.request = request; } @Override public String getParameter(String name) { String value = this.request.getParameter(name); if (value == null) { return null; } //如果不是get方法的,直接返回就行了 if (!this.request.getMethod().equalsIgnoreCase("get")) { return value; } try { //進(jìn)來了就說明是get方法,把亂碼的數(shù)據(jù) value = new String(value.getBytes("ISO8859-1"), this.request.getCharacterEncoding()); return value ; } catch (UnsupportedEncodingException e) { e.printStackTrace(); throw new RuntimeException("不支持該編碼"); } } }
將被增強(qiáng)的request對象傳遞給目標(biāo)資源,那么目標(biāo)資源使用request調(diào)用getParameter()方法的時候,獲取得到的就是中文數(shù)據(jù),而不是亂碼了!
//將request和response強(qiáng)轉(zhuǎn)成http協(xié)議的 HttpServletRequest httpServletRequest = (HttpServletRequest) req; HttpServletResponse httpServletResponse = (HttpServletResponse) resp; httpServletRequest.setCharacterEncoding("UTF-8"); httpServletResponse.setCharacterEncoding("UTF-8"); httpServletResponse.setContentType("text/html;charset=UTF-8"); MyRequest myRequest = new MyRequest(httpServletRequest); //傳遞給目標(biāo)資源的request是被增強(qiáng)后的。 chain.doFilter(myRequest, httpServletResponse);第二次測試
使用get方式傳遞中文數(shù)據(jù)給服務(wù)器
敏感詞的過濾器
如果用戶輸入了敏感詞(傻b、尼瑪、操蛋等等不文明語言時),我們要將這些不文明用于屏蔽掉,替換成符號!
要實(shí)現(xiàn)這樣的功能也很簡單,用戶輸入的敏感詞肯定是在getParameter()獲取的,我們在getParameter()得到這些數(shù)據(jù)的時候,判斷有沒有敏感詞匯,如果有就替換掉就好了!簡單來說:也是要增強(qiáng)request對象
增強(qiáng)request對象class MyDirtyRequest extends HttpServletRequestWrapper { HttpServletRequest request; //定義一堆敏感詞匯 private List開發(fā)過濾器list = Arrays.asList("傻b", "尼瑪", "操蛋"); public MyDirtyRequest(HttpServletRequest request) { super(request); this.request = request; } @Override public String getParameter(String name) { String value = this.request.getParameter(name); if (value == null) { return null; } //遍歷list集合,看看獲取得到的數(shù)據(jù)有沒有敏感詞匯 for (String s : list) { if (s.equals(value)) { value = "*****"; } } return value ; } }
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { //將request和response強(qiáng)轉(zhuǎn)成http協(xié)議的 HttpServletRequest httpServletRequest = (HttpServletRequest) req; HttpServletResponse httpServletResponse = (HttpServletResponse) resp; MyDirtyRequest dirtyRequest = new MyDirtyRequest(httpServletRequest); //傳送給目標(biāo)資源的是被增強(qiáng)后的request對象 chain.doFilter(dirtyRequest, httpServletResponse); }測試 壓縮資源過濾器
按照過濾器的執(zhí)行順序:執(zhí)行完目標(biāo)資源,過濾器后面的代碼還會執(zhí)行。所以,我們在過濾器中可以獲取執(zhí)行完目標(biāo)資源后的response對象!
我們知道sun公司提供的response對象調(diào)用write()方法,是直接把數(shù)據(jù)返回給瀏覽器的。我們要想實(shí)現(xiàn)壓縮的功能,write()方法就不能直接把數(shù)據(jù)寫到瀏覽器上!
這和上面是類似的,過濾器傳遞給目標(biāo)資源的response對象就需要被我們增強(qiáng),使得目標(biāo)資源調(diào)用writer()方法的時候不把數(shù)據(jù)直接寫到瀏覽器上!
增強(qiáng)response對象response對象可能會使用PrintWriter或者ServletOutputStream對象來調(diào)用writer()方法的,所以我們增強(qiáng)response對象的時候,需要把getOutputSteam和getWriter()重寫
class MyResponse extends HttpServletResponseWrapper{ HttpServletResponse response; public MyResponse(HttpServletResponse response) { super(response); this.response = response; } @Override public ServletOutputStream getOutputStream() throws IOException { return super.getOutputStream(); } @Override public PrintWriter getWriter() throws IOException { return super.getWriter(); } }
接下來,ServletOutputSteam要調(diào)用writer()方法,使得它不會把數(shù)據(jù)寫到瀏覽器上。這又要我們增強(qiáng)一遍了!
增強(qiáng)ServletOutputSteam/*增強(qiáng)ServletOutputSteam,讓writer方法不把數(shù)據(jù)直接返回給瀏覽器*/ class MyServletOutputStream extends ServletOutputStream{ private ByteArrayOutputStream byteArrayOutputStream; public MyServletOutputStream(ByteArrayOutputStream byteArrayOutputStream) { this.byteArrayOutputStream = byteArrayOutputStream; } //當(dāng)調(diào)用write()方法的時候,其實(shí)是把數(shù)據(jù)寫byteArrayOutputSteam上 @Override public void write(int b) throws IOException { this.byteArrayOutputStream.write(b); } }增強(qiáng)PrintWriter
PrintWriter對象就好辦了,它本來就是一個包裝類,看它的構(gòu)造方法,我們直接可以把ByteArrayOutputSteam傳遞給PrintWriter上。
@Override public PrintWriter getWriter() throws IOException { printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream, this.response.getCharacterEncoding())); return printWriter; }獲取緩存數(shù)據(jù)
我們把數(shù)據(jù)都寫在了ByteArrayOutputSteam上了,應(yīng)該提供方法給外界過去緩存中的數(shù)據(jù)!
public byte[] getBuffer() { try { //防止數(shù)據(jù)在緩存中,要刷新一下! if (printWriter != null) { printWriter.close(); } if (byteArrayOutputStream != null) { byteArrayOutputStream.flush(); return byteArrayOutputStream.toByteArray(); } } catch (IOException e) { e.printStackTrace(); } return null; }增強(qiáng)response的完整代碼
class MyResponse extends HttpServletResponseWrapper{ private ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); private PrintWriter printWriter ; private HttpServletResponse response; public MyResponse(HttpServletResponse response) { super(response); this.response = response; } @Override public ServletOutputStream getOutputStream() throws IOException { //這個的ServletOutputSteam對象調(diào)用write()方法的時候,把數(shù)據(jù)是寫在byteArrayOutputSteam上的 return new MyServletOutputStream(byteArrayOutputStream); } @Override public PrintWriter getWriter() throws IOException { printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream, this.response.getCharacterEncoding())); return printWriter; } public byte[] getBuffer() { try { //防止數(shù)據(jù)在緩存中,要刷新一下! if (printWriter != null) { printWriter.close(); } if (byteArrayOutputStream != null) { byteArrayOutputStream.flush(); return byteArrayOutputStream.toByteArray(); } } catch (IOException e) { e.printStackTrace(); } return null; } }過濾器
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; MyResponse myResponse = new MyResponse(response); //把被增強(qiáng)的response對象傳遞進(jìn)去,目標(biāo)資源調(diào)用write()方法的時候就不會直接把數(shù)據(jù)寫在瀏覽器上了 chain.doFilter(request, myResponse); //得到目標(biāo)資源想要返回給瀏覽器的數(shù)據(jù) byte[] bytes = myResponse.getBuffer(); //輸出原來的大小 System.out.println("壓縮前:"+bytes.length); //使用GZIP來壓縮資源,再返回給瀏覽器 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream); gzipOutputStream.write(bytes); gzipOutputStream.flush(); //得到壓縮后的數(shù)據(jù) byte[] gzip = byteArrayOutputStream.toByteArray(); System.out.println("壓縮后:" + gzip.length); //還要設(shè)置頭,告訴瀏覽器,這是壓縮數(shù)據(jù)! response.setHeader("content-encoding", "gzip"); response.setContentLength(gzip.length); response.getOutputStream().write(gzip); }測試
在Servlet上輸出一大段文字:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().write("fdshfidsuhfidusfhuidsfhuidshdsuifhsd" + "uifhsduifffffdshfidsuhfidusfhuidsfhuidshdsuif" + "hsduifhsduifffffdshfidsuhfidusfhuidsfhuidshd" + "suifhsduifhsduifffffdshfidsuhfidusfhuidsfhuidsh" + "dsuifhsduifhsduifffffdshfidsuhfidusfhuidsfhuids" + "hdsuifhsduifhsduifffffdshfidsuhfidusfhuidsfhuid" + "shdsuifhsduifhsduiffdshfidsuhfidusfhuidsfhuids" + "hdsuifhsduifhsduifffffdshfidsuhfidusfhuidsfhui" + "dshdsuifhsduifhsduifffffdshfidsuhfidusfhuidsfh" + "uidshdsuifhsduifhsduifffffdshfidsuhfidusfhuids" + "fhuidshdsuifhsduifhsduifffffdshfidsuhfidusfhuid" + "sfhuidshdsuifhsduifhsduifffffdshfidsuhfidusfhui" + "dsfhuidshdsuifhsduifhsduifffffdshfidsuhfidusfh" + "uidsfhuidshdsuifhsduifhsduifffffdshfidsuhfidusf" + "huidsfhuidshdsuifhsduifhsduifffffdshfidsuhfidus" + "fhuidsfhuidshdsuifhsduifhsduifffffdshfidsuhfid" + "usfhuidsfhuidshdsuifhsduifhsduifffffdshfidsuhf" + "idusfhuidsfhuidshdsuifhsduifhsd" + "uifffffdshfidsuhfidusfhuidsfhuidshdsuifhsduifhsduifffffff"); }
效果:
HTML轉(zhuǎn)義過濾器只要把getParameter()獲取得到的數(shù)據(jù)轉(zhuǎn)義一遍,就可以完成功能了。
增強(qiáng)requestclass MyHtmlRequest extends HttpServletRequestWrapper{ private HttpServletRequest request; public MyHtmlRequest(HttpServletRequest request) { super(request); this.request = request; } @Override public String getParameter(String name) { String value = this.request.getParameter(name); return this.Filter(value); } public String Filter(String message) { if (message == null) return (null); char content[] = new char[message.length()]; message.getChars(0, message.length(), content, 0); StringBuffer result = new StringBuffer(content.length + 50); for (int i = 0; i < content.length; i++) { switch (content[i]) { case "<": result.append("<"); break; case ">": result.append(">"); break; case "&": result.append("&"); break; case """: result.append("""); break; default: result.append(content[i]); } } return (result.toString()); } }過濾器
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; MyHtmlRequest myHtmlRequest = new MyHtmlRequest(request); //傳入的是被增強(qiáng)的request! chain.doFilter(myHtmlRequest, response); }測試
jsp代碼:
Servlet代碼:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String value = request.getParameter("username"); response.getWriter().write(value); }緩存數(shù)據(jù)到內(nèi)存中
在前面我們已經(jīng)做過了,讓瀏覽器不緩存數(shù)據(jù)【驗(yàn)證碼的圖片是不應(yīng)該緩存的】。
現(xiàn)在我們要做的是:緩存數(shù)據(jù)到內(nèi)存中【如果某個資源重復(fù)使用,不輕易變化,應(yīng)該緩存到內(nèi)存中】
這個和壓縮數(shù)據(jù)的Filter非常類似的,因?yàn)?strong>讓數(shù)據(jù)不直接輸出給瀏覽器,把數(shù)據(jù)用一個容器(ByteArrayOutputSteam)存起來。如果已經(jīng)有緩存了,就取緩存的。沒有緩存就執(zhí)行目標(biāo)資源!
增強(qiáng)response對象class MyResponse extends HttpServletResponseWrapper { private ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); private PrintWriter printWriter ; private HttpServletResponse response; public MyResponse(HttpServletResponse response) { super(response); this.response = response; } @Override public ServletOutputStream getOutputStream() throws IOException { //這個的ServletOutputSteam對象調(diào)用write()方法的時候,把數(shù)據(jù)是寫在byteArrayOutputSteam上的 return new MyServletOutputStream(byteArrayOutputStream); } @Override public PrintWriter getWriter() throws IOException { printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream, this.response.getCharacterEncoding())); return printWriter; } public byte[] getBuffer() { try { //防止數(shù)據(jù)在緩存中,要刷新一下! if (printWriter != null) { printWriter.close(); } if (byteArrayOutputStream != null) { byteArrayOutputStream.flush(); return byteArrayOutputStream.toByteArray(); } } catch (IOException e) { e.printStackTrace(); } return null; } } //增強(qiáng)ServletOutputSteam,讓writer方法不把數(shù)據(jù)直接返回給瀏覽器 class MyServletOutputStream extends ServletOutputStream { private ByteArrayOutputStream byteArrayOutputStream; public MyServletOutputStream(ByteArrayOutputStream byteArrayOutputStream) { this.byteArrayOutputStream = byteArrayOutputStream; } //當(dāng)調(diào)用write()方法的時候,其實(shí)是把數(shù)據(jù)寫byteArrayOutputSteam上 @Override public void write(int b) throws IOException { this.byteArrayOutputStream.write(b); } }過濾器
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { //定義一個Map集合,key為頁面的地址,value為內(nèi)存的緩存 Map測試map = new HashMap<>(); HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; //得到客戶端想要請求的資源 String uri = request.getRequestURI(); byte[] bytes = map.get(uri); //如果有緩存,直接返回給瀏覽器就行了,就不用執(zhí)行目標(biāo)資源了 if (bytes != null) { response.getOutputStream().write(bytes); return ; } //如果沒有緩存,就讓目標(biāo)執(zhí)行 MyResponse myResponse = new MyResponse(response); chain.doFilter(request, myResponse); //得到目標(biāo)資源想要發(fā)送給瀏覽器的數(shù)據(jù) byte[] b = myResponse.getBuffer(); //把數(shù)據(jù)存到集合中 map.put(uri, b); //把數(shù)據(jù)返回給瀏覽器 response.getOutputStream().write(b); }
盡管是刷新,獲取得到的也是從緩存拿到的數(shù)據(jù)!
如果文章有錯的地方歡迎指正,大家互相交流。習(xí)慣在微信看技術(shù)文章的同學(xué),可以關(guān)注微信公眾號:Java3y
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/70938.html
摘要:正則不是必須的,任何東西我們都可以不用正則用其他方式實(shí)現(xiàn)。正則中的轉(zhuǎn)義尋找第一個出現(xiàn)的數(shù)字正則中的轉(zhuǎn)義代表數(shù)字索引數(shù)字出現(xiàn)在索引的位置只能找到字母出現(xiàn)的索引位置。所以我們就需要寫一個過濾標(biāo)簽的方法。點(diǎn)代表任意字符,在正則里勁量不要用。 寫一個最簡單的正則 // var re=new RegExp(a);//js風(fēng)格 var re=/a/;//perl風(fēng)格,古老語言現(xiàn)在沒人用了 // v...
摘要:我把這個領(lǐng)域的東西寫成了一個系列,以后還會繼續(xù)完善下去安全一同源策略與跨域安全二攻擊安全三攻擊 上文說完了CSRF攻擊,本文繼續(xù)研究它的兄弟XSS攻擊。 什么是XSS攻擊 XSS攻擊全名(Cross-Site-Script)跨域腳本攻擊,為了跟CSS(Cascading-Style-Sheet)區(qū)分開來,所以縮寫是XSS。 XSS攻擊的原理 上一節(jié)說道的CSRF攻擊是利用的是偽請求,這...
摘要:我把這個領(lǐng)域的東西寫成了一個系列,以后還會繼續(xù)完善下去安全一同源策略與跨域安全二攻擊安全三攻擊 上文說完了CSRF攻擊,本文繼續(xù)研究它的兄弟XSS攻擊。 什么是XSS攻擊 XSS攻擊全名(Cross-Site-Script)跨域腳本攻擊,為了跟CSS(Cascading-Style-Sheet)區(qū)分開來,所以縮寫是XSS。 XSS攻擊的原理 上一節(jié)說道的CSRF攻擊是利用的是偽請求,這...
摘要:前言由于寫的文章已經(jīng)是有點(diǎn)多了,為了自己和大家的檢索方便,于是我就做了這么一個博客導(dǎo)航。 前言 由于寫的文章已經(jīng)是有點(diǎn)多了,為了自己和大家的檢索方便,于是我就做了這么一個博客導(dǎo)航。 由于更新比較頻繁,因此隔一段時間才會更新目錄導(dǎo)航哦~想要獲取最新原創(chuàng)的技術(shù)文章歡迎關(guān)注我的公眾號:Java3y Java3y文章目錄導(dǎo)航 Java基礎(chǔ) 泛型就這么簡單 注解就這么簡單 Druid數(shù)據(jù)庫連接池...
摘要:一背景團(tuán)隊(duì)最近頻繁遭受網(wǎng)絡(luò)攻擊,引起了技術(shù)負(fù)責(zé)人的重視,筆者在團(tuán)隊(duì)中相對來說更懂安全,因此花了點(diǎn)時間編輯了一份安全開發(fā)自檢清單,覺得應(yīng)該也有不少讀者有需要,所以將其分享出來。 一、背景 團(tuán)隊(duì)最近頻繁遭受網(wǎng)絡(luò)攻擊,引起了技術(shù)負(fù)責(zé)人的重視,筆者在團(tuán)隊(duì)中相對來說更懂安全,因此花了點(diǎn)時間編輯了一份安全開發(fā)自檢清單,覺得應(yīng)該也有不少讀者有需要,所以將其分享出來。 二、編碼安全 2.1 輸入驗(yàn)證 ...
閱讀 2532·2021-09-24 10:29
閱讀 3817·2021-09-22 15:46
閱讀 2584·2021-09-04 16:41
閱讀 2990·2019-08-30 15:53
閱讀 1271·2019-08-30 14:24
閱讀 3064·2019-08-30 13:19
閱讀 2181·2019-08-29 14:17
閱讀 3532·2019-08-29 12:55