Web之过滤器Filter
发布日期:2021-07-27 04:56:03 浏览次数:8 分类:技术文章

本文共 15129 字,大约阅读时间需要 50 分钟。

一、概述

Filter也称之为过滤器,它是Servlet技术中最激动人心的技术,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态html文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。

Servlet API中提供了一个Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则把这个java类称之为过滤器Filter。通过Filter技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截。

二、基本使用方法

2.1 步骤

  1. 创建Filter类对象
  2. 注解或者xml形式修改属性,设置拦截url
  3. 重写doFilter方法:chain.doFilter(request, response) 表示可以通行

2.2 注意的问题

  1. 注解和xml的配置,可以配置initParam参数,可以配置url,可以在init方法中获取初始参数,这些都和servlet一样,参考
  2. 多个过滤器存在的时候,先后顺序,在xml文件中是【mapping映射】属性先后顺序决定,在注解中是,类文件名称决定。
  3. servlet和filter的url的意义不一样,前者是用来被访问,后者是用于过滤拦截,比如【*.jsp】标示,凡是jsp文件都要走过滤器,如果【chain.doFilter(request, response) 】方法没有,表示所有jsp文件不通行。
  4. 两个过滤器可以拦截同一个路径,但是servlet不可以。
  5. 不能写【"/"】路径符号
  6. doFilter方法内,必须将req和resp强转为【HttpServletRequest request】和【HttpServletResponse response】

2.3 过滤流程【重点】

两个过滤器,如下:

过滤器2,依靠xml文件配置url

myfilter2
MyFilter2
name
张三
myfilter2
*.html
//过滤器2public class MyFilter2 implements Filter {
public void destroy() {
System.out.println("销毁了myfilter2"); } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("处理前Myfilter2"); //chain.doFilter(req, resp); System.out.println("处理后myfilter2"); } public void init(FilterConfig config) throws ServletException {
System.out.println("初始化myfilter2"); /*可以在xml设置属性,并获取*/ String name = config.getInitParameter("name"); System.out.println(name); }}//过滤器1,注解配置@WebFilter(filterName = "MyFilter1",value = "/hello.jsp",initParams = {
@WebInitParam(name = "age", value = "40")})public class MyFilter1 implements Filter {
@Override public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("初始化myFilter1"); /*可以在注解里面设置属性,并获取,优先级是xml最高*/ String age = filterConfig.getInitParameter("age"); System.out.println(age); } @Override /** * 过滤器链FilterChain */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("过滤前处理MyFilter1_before。。。。"); //chain.doFilter(request, response); //放行目标资源,允许执行,没有该方法,表示拦截 System.out.println("过滤后处理MyFilter1_after。。。。"); } @Override public void destroy() {
//销毁 System.out.println("销毁了myfilter1"); }}

两个过滤器拦截不同,过滤器1是【"/hello.jsp"】,过滤器2是【"*.html"】

在这里插入图片描述
来回都可以过滤,资源1和资源2就是url部分,想要处理哪个资源就将其放入。可以双向过滤,如果要在来时过滤,则在过滤器中使用【request】方法,反之,使用【response】方法。

三、案例

3.1 自动登录案例

当前端页面,用户勾选了【记住帐号密码】,浏览器会将用户的登录信息放到【cookie】,加密保存,下次登录的时候,直接检查cookie,如果匹配则直接登录。

这里要注意还有一种情况:用户登录以后,没有关掉浏览器,会话还存在的情况下,属于已经登录状态,无须再次登录。这就需要用session进行本次会话的监视。

这里的自动登录,是不需要用户输入了,但其实还是需要将信息给到服务器,让服务器帮忙登录的

关于cookie和session的使用方式,详见

正常访问流程

在这里插入图片描述

这样的话,如果浏览器直接访问主页,也能进入主页,但是没有登录信息,就是登录失败。
在这里插入图片描述

自动登录,信息在cookie,过滤器通过cookie比对,成功后,将用户名放入session,这样,主页可以拿到用户名了,也就登录成功了

在这里插入图片描述

登录界面

    用户登录    

用户登录

用户名
密码
记住我

主页

    hello    

hello

欢迎${username}登陆

登录servlet

@WebServlet(name = "LoginServlet",value = "/hello.jsp")public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//处理 request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); String username = request.getParameter("username"); String password = request.getParameter("password"); String auto = request.getParameter("auto"); //判断 if (username.equals("admin")&&password.equals("888")) {
//登录成功,这里将用户名作为唯一标示放到session request.getSession().setAttribute("username", username); //判断有没有勾选auto if (auto != null) {
//勾选了,则创建一个cookie //Base64账户密码加密处理,注意要有返回字符串 String info = username + "#" + password; info = Base64.getEncoder().encodeToString(info.getBytes()); //将加密的字符串放入cookie Cookie cookie = new Cookie("info", info); //设置cookie的有效期 cookie.setMaxAge(60 * 60 * 24 * 7); cookie.setPath("/"); cookie.setHttpOnly(true); //将cookie添加进去 response.addCookie(cookie); System.out.println("服务器创建了cookie"); } //重定向给hello.jsp,即主页 response.sendRedirect("hello.jsp"); } else {
response.getWriter().println("登录失败"); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response); }}
@WebFilter(filterName = "AutoLoginFilter", value = "/hello.jsp")public class AutoLoginFilter implements Filter {
public void destroy() {
} public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; //自动登录 //判断当前是否已经登录? Object username = request.getSession().getAttribute("username"); if (username != null) {
//已经登录,不用拦截,直接放行 chain.doFilter(req,resp); return; } //否则获取cookie Cookie[] cookies = request.getCookies(); if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals("info")) {
String value = cookie.getValue(); byte[] bytes = Base64.getDecoder().decode(value); String info = new String(bytes); String[] infos = info.split("#"); //用户名和密码可能会被篡改,这里必须再次判断 if (infos[0].equals("admin") && infos[1].equals("888")) {
//成功,将用户名存入session,保证已经处于登录状态,可以走【username != null】,不用每次都cookie判断 request.getSession().setAttribute("username", infos[0]); //放行 chain.doFilter(req, resp); return; } else {
//删除cookie,同名覆盖,修改有效期 Cookie cookie1 = new Cookie("info", ""); cookie1.setMaxAge(0); cookie1.setHttpOnly(true); response.addCookie(cookie); } } } } chain.doFilter(req, resp); //失败放行,显示网站,但是未登录状态 } public void init(FilterConfig config) throws ServletException {
}}

3.2 脏话过滤案例

脏话是由前端发过来的,然后在servlet资源中处理,所以要对servlet进行过滤,过滤的方式是【重写getParameter】方法,这样servlet进行处理的时候会调用重写的getParameter,实现过滤。

这里会使用到【装饰者设计模式】,重写getParameter方法,增强功能,详见。

前端

    聊天窗口    

聊天窗口

昵称:
聊天内容:

服务器

@WebServlet(name = "ChatServlet", value = "/chatservlet")public class ChatServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8"); String nickname = request.getParameter("nickname"); String content = request.getParameter("content"); System.out.println(nickname + "说:" + content); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response); }}

过滤器,返回时过滤,拦截servlet的getParameter方法,重写

@WebFilter(filterName = "DirtyFilter", value = "/chatservlet")public class DirtyFilter implements Filter {
public void destroy() {
} public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println("开始过滤"); //增强类的功能:1.继承重写 2.使用装饰者模式 3.动态代理 //重写getParameter方法 MyRequest myRequest = new MyRequest((HttpServletRequest) req); //将重写后的request放入,并放行,这样chatservlet执行getParameter执行方法的时候,使用的是重写的方法,返回的是经过脏字处理的值 chain.doFilter(myRequest, resp); } public void init(FilterConfig config) throws ServletException {
} static class MyRequest extends HttpServletRequestWrapper {
private List
dirtywords = null; //脏字集合 //重写构造方法 public MyRequest(HttpServletRequest request) {
super(request); dirtywords = Arrays.asList("艹", "操"); } //重写getParameter方法 @Override public String getParameter(String name) {
//重写,进行过滤 String value = super.getParameter(name); for (String dirtyword : dirtywords) {
//遍历脏字,当value中出现相关脏字就全部替换 value = value.replace(dirtyword, "**"); } return value; } }}

3.3 图片缓存案例【了解】

一般的浏览器,都会优先从缓存中读取数据,而非不停的向服务器请求,这会导致一个问题,服务器更新了数据,但是浏览器却没有及时更新,目前已经解决了这个问题。

解决办法:

在这里插入图片描述
目前谷歌是这么处理的,其他浏览器依然会优先到缓存处理。

但是,对于图片展示,则不需要每次都想服务器请求,为了提高效率,这里可以设置时间,只要时间未到,就到缓存查找,时间过期则请求服务器(注:谷歌浏览器办不到)。

//这里拦截所有该格式文件@WebFilter(filterName = "ImgCacheFilter", value = {
"*.jpg", "*.png", "*.bmp", "*.html", "*.css", "*.js"})public class ImgCacheFilter implements Filter {
public void destroy() {
} public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//带缓存,因为图片不用每次都访问服务器,除去谷歌浏览器,其他浏览器会直接走缓存,这里设置10分后,再次刷新的时候,去服务器访问 HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; response.setDateHeader("Expires", System.currentTimeMillis()+600000); //有效期Expires 毫秒十分钟 response.setHeader("Cache-Control", "max-age=600"); //600秒,10分钟,请求头缓存控制 chain.doFilter(req, resp); } public void init(FilterConfig config) throws ServletException {
}}

3.4 防盗链案例

A为正版网站,各种资源,B为盗版网站,为了盈利,B网站页面的图片排版等都是链接的A网站,而图片对应的实际购买链接是B自己的,这就是盗图,盗链。

解决办法:

网站的每一张图片都是要请求服务器的,原因可以参考第九题。既然每次都要请求,那么就需要过滤器在来之前过滤图片的链接。

http 协议中,如果从一个网页跳到另一个网页,http 头字段里面会带个 Referer。图片服务器通过检测 Referer 是否来自规定域名,来进行防盗链。

//将各种格式的图片进行过滤,记住,每一个图片都需要请求,这里只需要操作图片,不需要操作网页@WebFilter(filterName = "StealFilter", value = {
"*.png", "*.jpg", "*.bmp"})public class StealFilter implements Filter {
public void destroy() {
} public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse)resp; //拿到图片的请求头 String referer = request.getHeader("referer"); System.out.println(referer); //这是本服务器的图片链接头,后面可能是图片的名称,但是不是判断依据 String localurl = "http://localhost:8080" + request.getContextPath(); System.out.println(localurl); //本网站访问,可以度过,图片请求头链接的开头是localurl if (referer != null && referer.startsWith(localurl)) {
System.out.println("通过了"); chain.doFilter(req, resp);//放行 return; } else {
//否则不放行,在访问服务器之前直接拦截,返回一个“盗链可耻”的图片以示警告 request.getRequestDispatcher("/img/wow8.jpg").forward(request, response); return; } } public void init(FilterConfig config) throws ServletException {
}}
    新闻页面    

震惊!!!某人去泰国居然遇到这种事情

3.5 文件压缩传输案例

浏览器请求的文件,服务器都会直接从数据库调取,然后返回,一般对于大文件,可以将其压缩,然后返回给浏览器,这时浏览器进行解压,可以提高传输效率。

如果需要压缩的资源在网页中,则url为网页,由于是将网页返回的资源进行压缩处理,所以使用response方法处理。

这里重写了【getWriter】方法,不直接将结果响应给浏览器,而是将其转到内存流,利用流,进行压缩处理,然后将response放入chain.doFilter()方法中,放行。

@WebFilter(filterName = "ZipFilter", value = "*.jsp")public class ZipFilter implements Filter {
public void destroy() {
} public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
MyResponse response = new MyResponse((HttpServletResponse) resp); chain.doFilter(req, response); //来时放行,返回时要走自定义的重写response //获取baos ByteArrayOutputStream baos = response.getBaos(); System.out.println("原始大小:" + baos.toByteArray().length); //压缩流压缩 ByteArrayOutputStream newbaos = new ByteArrayOutputStream(); //GZIP压缩流 GZIPOutputStream gzip = new GZIPOutputStream(newbaos); gzip.write(baos.toByteArray()); gzip.flush(); gzip.close(); System.out.println("压缩之后:" + newbaos.toByteArray().length); //把压缩之后的数据发给浏览器 //告知浏览器是压缩文件,这样浏览器自动解压 ((HttpServletResponse) resp).setHeader("Content-Encoding", "gzip"); resp.getOutputStream().write(newbaos.toByteArray()); } public void init(FilterConfig config) throws ServletException {
} static class MyResponse extends HttpServletResponseWrapper {
private ByteArrayOutputStream baos; private PrintWriter pw; /** * 重写getWriter方法,转到内存流 */ public MyResponse(HttpServletResponse response) {
super(response); baos = new ByteArrayOutputStream(); try {
//处理编码,转换流 pw = new PrintWriter(new OutputStreamWriter(baos, "utf-8")); } catch (UnsupportedEncodingException e) {
e.printStackTrace(); } } @Override public PrintWriter getWriter() throws IOException {
return pw; //返回的是自定义的pw,不再是super里的getWriter } //该方法主要是获取字节流,方便测试,查看压缩前后的文件大小 public ByteArrayOutputStream getBaos() {
if (pw != null) {
pw.flush(); } return baos; } }}

3.6 解决编码问题【了解】

一般会出现兼容问题导致编码配置失败,或者用户忘记配置的情况,这里采用xml

CharacterEncodingFilter
filter.CharacterEncodingFilter
charset
utf-8
CharacterEncodingFilter
/*

过滤器

@WebFilter(filterName = "CharacterEncodingFilter", value = "/*")public class CharacterEncodingFilter implements Filter {
/*总的思想就是如果用户配置了编码,就按照配置编码设置,否则给个默认编码,保证必须有编码*/ //xml中配置的编码 private String charset = null; //默认的编码 private String defaultCharset = "utf-8"; public void destroy() {
} public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
if (charset == null) {
charset = defaultCharset; } req.setCharacterEncoding(charset); chain.doFilter(req, resp); } public void init(FilterConfig config) throws ServletException {
charset = config.getInitParameter("charset"); //从xml中获取编码value }}

转载地址:https://blog.csdn.net/qq_45337431/article/details/100586786 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:JSON和AJAX
下一篇:行为型设计模式——观察者模式

发表评论

最新留言

做的很好,不错不错
[***.243.131.199]2024年09月11日 10时24分43秒