Filter 常用例子
3. 登录验证 Filter
我们在浏览网站的时候,有时候是没有登录的,或者因为登录很久后自动掉出来。可能有一些请求需要判断用户是否处于登录状态 ,以及该用户的权限。Java Web 程序一般使用 session 或者 cookie 来记录用户是否登录。登录验证 Filter 是将 request 提交给 servlet 之前,对 session 或者 cookie 进行校验。如果没有相应的登录信息,或者权限不够,则进行相应的处理,比如跳转到登录界面。
下面的例子中,我们仅判断用户是否处于登录状态。
PrivilegeFilter.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package com.anye137.filter; import java.io.IOException; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.*;
@WebFilter(filterName="privilegeFilter",urlPatterns="/jsp/afterLogin.jsp") public class PrivilegeFilter implements Filter{ public void init(FilterConfig config) throws ServletException{} public void destroy(){} public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException{ HttpServletRequest req = (HttpServletRequest)request; HttpServletResponse res = (HttpServletResponse)response; HttpSession session = req.getSession(); String name = (String) session.getAttribute("userName"); if(name==null || name.trim().equals("")) res.sendRedirect(req.getContextPath()+"/jsp/login.jsp"); else chain.doFilter(req, response); }
}
|
登录界面 login.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title></title> </head> <body> <form action="${pageContext.request.contextPath}/loginServlet" method="post"> 姓名:<input type="text" name="name"/><br> <input type="submit" value="登录"/><br> </form> </body> </html>
|
处理登录的 servlet LoginServlet.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package com.anye137.test; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*;
@WebServlet("/loginServlet") public class LoginServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); session.setAttribute("userName", request.getParameter("userName")); response.sendRedirect("jsp/afterLogin.jsp"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
|
登录后的界面afterLogin.jsp
1 2 3 4 5 6 7 8 9 10 11 12
| <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title></title> </head> <body> 用户名: <%= session.getAttribute("name") %> 您已成功登陆 </body> </html>
|
运行效果:通过login.jsp
页面可以登录进入 afterLogin.jsp
页面,如果没有登录直接输入afterLogin.jsp
的网址,则会跳转到登录界面。
注意,上面代码中并没有设置 request 和 response 编码,因为我在上一篇文章中已经写好了 CharacterEncodingFilter
。当然,如果不想用字符编码 Filter,我们也可以在 servlet 中简单设置下编码。
4.内容替换 Filter
有时候需要对网站的内容进行控制,防止输出非法内容或者敏感内容。常规的解决办法是在保存进数据库之前对非法敏感内容进行替环,或者在 servlet 里输出到客户端时进行替环。这两种解决方案都有很大的局限性,例如每个 servlet 都要进行替换、工作量大,与业务耦合比较严重等。
使用内容替换 Filter 则方便得多,其工作原理是:在 servlet 内将内容输出到 response 时,response 将内容缓存起来,在 Filter 中进行替换,然后再输出到客户端浏览器。由于默认的 response 并不能严格地缓存输出内容,因此需要自定义一个具备缓存功能的 response。
OutputReplaceFilter.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| package com.anye137.filter; import java.io.*; import java.util.Properties; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.annotation.WebInitParam; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper;
@WebFilter( filterName="outputReplaceFilter", urlPatterns="/*", initParams={@WebInitParam(name="file",value="/WEB-INF/sensitive.properties")} )
public class OutputReplaceFilter implements Filter { private Properties pp = new Properties(); public OutputReplaceFilter() {} public void init(FilterConfig config) throws ServletException { String file = config.getInitParameter("file"); String realPath = config.getServletContext().getRealPath(file); try{ BufferedInputStream in = new BufferedInputStream(new FileInputStream(realPath)); pp.load(new InputStreamReader(in, "utf-8")); }catch(IOException e){} } public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("replace"); HttpServletResponse res = (HttpServletResponse)response; OutputReplaceResponse orr = new OutputReplaceResponse(res); chain.doFilter(request, orr); String output = orr.getCharArrayWriter().toString(); for(Object obj: pp.keySet()){ String key = (String)obj; output = output.replace(key, pp.getProperty(key)); } PrintWriter out = response.getWriter(); out.write(output); } class OutputReplaceResponse extends HttpServletResponseWrapper{ } }
|
自定义的response
类OutputReplaceResponse
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class OutputReplaceResponse extends HttpServletResponseWrapper{ private CharArrayWriter caw=new CharArrayWriter(); public OutputReplaceResponse(HttpServletResponse response) { super(response); } @Override public PrintWriter getWriter() throws IOException{ return new PrintWriter(caw); } public CharArrayWriter getCharArrayWriter(){ return caw; } }
|
本例中,自定义的 response 只是一个“伪装”的 response。servlet会通过它输出内容到客户端,但是它内部只是将内容缓存起来了,并没有真正输出到客户端。最终输出到客户端还是通过原来的 response 完成的。
词库配置 sensitive.properties
1 2 3 4 5 6
| # 自动更正 Chna = China # 自动替换 赌博 = ** 色情 = ** 情色 = **
|
我把 properties 文件设置为 utf-8 编码的 (eclipse中右键文件名,点Properties 设置就行了),在上面的 Filter 中是这样加载的:
1 2
| BufferedInputStream in = new BufferedInputStream(new FileInputStream(realPath)); pp.load(new InputStreamReader(in, "utf-8"));
|
使用 utf-8 编码的话,在 properties 文件中直接输入中文会正常显示。
如果把文件编码设置为 ISO-8859-1,这时在编辑器输入中文,只能显示出中文对应的 Unicode 编码,但是对后续的操作其实影响不大。加载此文件只需一行代码:
1
| pp.load(new FileInputStream(realPath));
|
测试页面testReplace.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <%@ page language="java" contentType="text/html; charset=UTF-8" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title></title> </head> <body> Chna <br/> <br/> 色情 <br/> 赌博 <br/> 情色 <br/> </body> </html>
|
在浏览器中输入测试页面网址,可以看到替换后的效果。
上面 Filter 是在init()
方法中加载 properties 文件的,Filter inti()
方法只在 web 程序第一次运行时调用一次。所以如果词库更新的话,需要重启 web 程序。
Filter 还有许多常用例子,例如缓存Filter、GZIP压缩Filter等,这里就不一一详述了。
本文详细代码见 Github
参考资料
(1) 《JavaWeb 王者归来》
(2) Java读写.properties文件实例,解决中文乱码问题 from CSDN