欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Filter

程序员文章站 2022-07-15 10:46:50
...

Filter介绍

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

javaee的API,filter接口
javax.servlet.filter

  • void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException
    • difilter是真正用于过滤的方法。filter也需要在web.xml文件中配置
    • 每个Filter都有对FilterConfig对象的访问权,可从该对象获得其初始化参数以及对它可以使用的ServletContext的引用,以便为过滤任务加载所需要的资源。
    • 每次由于对链末尾的某个资源的客户端请求而通过链传递请求/响应对时,容器都会调用Filter的doFilter 方法,传入此方法的FilterChain允许Filter将请求和响应传递到链中的下一个实体。
  • void init(FilterConfig filterConfig) throws ServletException
  • void destroy()

Filter

Filter入门

Filter创建步骤

  • 创建一个类,实现javax.servlet.Filter接口
  • 重写接口中的方法
  • 在web.xml文件中配置
public class DemoFilter implements Filter{
    //重写接口内的方法
    public void destriy(){

    }
    public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain) throws IOException,ServletException{
    System.out.println(“执行过滤操作”);
//放行,继续执行index.jsp
    chain.dofilter(request,response);


    }
    public void init(FilterConfig filterConfig) throws ServletException{

    }
}
//web.xml配置
<filter>
    <filter-name>demoFilter</filter-name>
    <filter-class>cn.itcast.web.filter.DemoFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>deFilter</filter-name>
    <url-pattern>/index.jsp</url-pattern>
    <!--访问index.jsp页面会被DemoFilter过滤-->
</filter-mapping>

访问index.jsp,结果展示:
Filter
设置参数chain:

Filter
执行结果:
Filter

实现过滤操作步骤:

  • <url-pattern>它是用于设置过滤的路径
  • 在doFilter方法的第三个参数FilterChain,它是用于控制是否继续往下执行(访问服务器端资源)的。

Filter的生命周期

  • 在javax.servlet.Filter接口中三个方法
    • public void init(FilterConfig filterConfig) throws ServletException
      • 初始化方法,只执行一次
    • public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain)
      • 真正进行拦截操作的方法
      • 通过chain.doFilter(request,response)向下执行
    • public void destroy()
      • 销毁操作

FilterConfig(参考ServletConfig)

  • 在Filter的init方法中有一个参数FilterConfig
  • FilterConfig作用也是获取Filter的相关配置信息
    • 初始化参数的获取
    • Filter的名称获取
    • ServletContext对象获取

接口FilterConfig

  • public String getFilterName()
    • 如部署描述符中定义的那样,返回此过滤器的过滤器名称
  • public ServletContext getServletContext()
    • 返回对调用者在其中执行操作的ServletContext的作用
  • public String getInitParameter(String name)
    • 返回包含指定初始化参数的值的String,如果参数不存在,则返回null
  • public java.util.Enumeration<E> getInitParameterNames()
    • 以String对象的Enumeration的形式返回过滤器初始化参数的名称,如果过滤器没有初始化参数,则返回一个空的Enumeration。
public void init(FilterConfig filterConfig) throws ServletException{
    //获取Filter名称
    String filterName = filterConfig.getFilterName();
    System.out.println(filterName);
    //获取初始化参数
    String encoding = filterConfig.getInitParameter("encoding");//utf-8
    System.out.println(encoding);//utf-8
//
    Enumeration<String> names = filterConfig.getInitParameterNames();
    while(names.hasMoreElements()){
        System.out.println(names.nextElements());
    }//encoding,username

        //获取ServletContext对象
        filterConfig.getServletContext();

}
//web.xml配置文件
<filter>
    <filter-name>demo2Filter</filter-name>
    <filter-class>cn.itcast.web.filter.Demo2Filter</filter-class>
    <!--初始化参数-->
    <init-param>
        <param-name>encoding</param-name>
        <param-value>utf-8</param-value>
    </init-param>
    <init-param>
        <param-name>username</param-name>
        <param-value>tom</param-value>
    </init-param>
</filter>

Filter详细配置信息

关于配置Filter

<filter>
    <filter-name></filter-name>
    <filter-class></filter-class>
    <init-param>
        <param-name></param-name>
        <param-value></param-value>
    </init-param>

    <filter-mapping>
        <filter-name></filter-name>
        <url-pattern></url-pattern>
    </filter-mapping>
</filter>

Filter链

  • 在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Filter链
  • web服务器根据Filter在web.xml文件中的注册顺序<mapping>,决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法 。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第二个filter,如果没有则调用目标资源。
  • 注意:
    • FilterChain.doFilter(request,response)它代表的是向下执行,如果下一个还是filter,那么访问这个filter,如果当前filter已经是整个链的末尾,那么访问web资源。
    • Filter的顺序由<Filter-mapping>的配置顺序决定。
//firstFilter
public class FirstFilter implements Filter{
    public void destroy{}
    public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain) throws IOException,ServletException{
        System.out.pritnln("firstFilter...");
        chain.doFilter(request,response);//向下放行
        System.out.println("firstFilter end ...");
    }
    public void init(FilterConfig filterConfig) throws ServletException{

    }
}
//secondfilter
public class SecondFilter implements Filter{
    public void destroy{}
    public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain) throws IOException,ServletException{
        System.out.pritnln("secondFilter...");
        chain.doFilter(request,response);//向下放行
        System.out.println("secondFilter end ...");
    }
    public void init(FilterConfig filterConfig) throws ServletException{
    }
}
//DemoServlet
public class DemoServlet extends HttpServlet{
    public void doGet(HttpServletRequest reequest,HttpServletResponse response) throws ServletException,IOException{
        Ssytem.out.pritnln("demo servlet");
    }
    public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
        doGet(request,response);
    }


}
<!--firstFilter-->
<filter-mapping>
        <filter-name>firstFilter</filter-name>
        <url-pattern>/index.jsp</url-pattern>
</filter-mapping>
<!--secondFilter-->
<filter-mapping>
        <filter-name>secondFilter</filter-name>
        <url-pattern>/index.jsp</url-pattern>
</filter-mapping>

Filter

Filter

<url-pattern>

  • 对于Fliter来说,它是用于确定拦截资源路径
  • <url-pattern>有几种方法:
    • 完全匹配 必须以“/”开始
    • 可以使用* 通配符
      • 目录匹配
        • /a/* /* 要求必须“/”开始
      • 扩展名匹配
        • .do .action 要求,不能以“/”开始,以*.xxx结束

简单的编码过滤(只针对于post请求)

  • 编写一个jsp页面
  • 编写一个servlet,在servlet中获取请求参数
  • 问题:乱码问题
    • 对于post解决方法:request.setCharacterEncoding(“utf-8”);
  • 创建一个Filter,在Filter中完成编码处理

禁用jsp页面缓存

  • 禁用页面缓存的目的:为了得到实时信息
  • 禁用jsp页面缓存的方式
    • 在jsp页面上设置
    • <meta http-equiv="pargma" content = "no-cache">
    • <meta http-equiv="cache-control" content="no-cache">
    • <meta http-equiv="expires" content="0">
    • 可以通过Filter来控制
    • 在Filter中设置
      • response.setHeader(“pragma”,”no-cache”);
      • response.setHeader(“cache-control”,”no-cache”);
      • response.setDateHeader(“expires”,-1);
    • filter的url-pattern
      • *.jsp(jsp格式的)
<filter-mapping>
    <filter-name>cacheFilter</filter-name>
    <url-pattern>*.jdp</url-pattern>
</filter-mapping>
public void doFilter(ServletRequest req,ServletResponse resp,FilterChain chain) throws IOException,ServletException{
    //强制转换
    HttpServletResponse request = (HttpServletRequest)req;
    HttpServletResponse response = (HttpServletResponse)resp;
    //操作
    response.setDateHeader("expires",0);
    //放行
    chain.doFilter(request,response);

}

设置图片缓存时间

  • 让图片缓存,缓存的目的是为了提高性能和效率
  • filter的url-pattern
    • *.bmp(图片的格式)
public void doFilter(ServletRequest req,ServletResponse resp,FilterChain chain) throws IOException,ServletException{
    //强制转换
    HttpServletResponse request = (HttpServletRequest)req;
    HttpServletResponse response = (HttpServletResponse)resp;
    //操作
    response.setDateHeader("expires",System.currentTimeMillis()+60*60*24*10*1000);//缓存10天
    //放行
    chain.doFilter(request,response);

}

自动登录案例

  • 在访问一个站点,登陆时勾选自动登陆(三个月内不用登录),操作系统后,关闭浏览器,过几天再次访问该站点时,直接进行登陆后状态。
  • 自动登陆原理:
    • 登录成功后,判断是否勾选了自动登录
    • 如果勾选了自动登录,将用户名与密码存储到cookie中
    • 做一个Filter,它拦截所有请求,当访问资源时,我们从cookie中获取用户名与密码,进行登录操作。

登录操作

  • 创建User数据库,user表,添加数据。
  • DataSourceUtils.java c3p0-config.xml User.java(javaBean)
  • login.jsp success.jsp
//success.jsp
<body>
    当前用户:${user.username}
</body>
//login.jsp
<body>
<!--展示异常信息-->
    ${requestScope["login.message"]}<br>
    <form action="${pageContext.request.contextPath}/login" method="post">
        username:<input type="text" name="username"><br>
        password:<input type="password" name="password"><br>
        <input type="submit" value="登录">
    </form>

</body>
//LoginServlet.java  url:login
public class LoginServlet extends HttpServlet{
    public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
        //得到请求参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        //登录 
        UserService service = new  UserService();
        try{
            User user = service.login(username,password);
            if(user != null){
                //登录成功
                request.getSession().setAttribute("user",user);
                response.senRedirect(request.getContextPath()+"/demo4/success.jsp")
                return;
            }else{
                request.setAttribute("login.message","用户名或密码错误");
                request.getRequestDispatche("/demo4/login.jsp").forward(request,response);
                return;
            }
        }catch(SQLException e){
            e.printStackTrace();
            request.setAttribute("login.message","登录失败");
            request.getRequestDispatche("/demo4/login.jsp").forward(request,response);
            return;
        }

    }

}
public class UserService throws SQLException{
    public User login(String username,String password){
        return new UserDao().findUserByUsernameAndPassword(username,password);


  }

}
public class UserDao{
    //根据用户名与密码查找用户
    public User findUserByUsernameAndPassword(String username,String password) throws SQLException{
        String sql = "select * from user where username=? and password=?";
        QueryRunner runner = new QueryRunner(DataSourceUtils.getDataSource());
        return runner.query(sql,new BeanHandler<User>(User.class),username,password);
    }
}

完成自动登录

  • 在页面上添加自动登录按钮
  • 在LoginServlet中
    *如果登录成功后,判断是否勾选了自动登录,如果勾选了,将用户名与密码存储到cookie中。
  • 创建一个AutoLoginFilter
//login.jsp
<body>
<!--展示异常信息-->
    ${requestScope["login.message"]}<br>
    <form action="${pageContext.request.contextPath}/login" method="post">
        username:<input type="text" name="username"><br>
        password:<input type="password" name="password"><br>
        <input type="checkbox" name="autologin" value="ok">自动登录<br>
        <input type="submit" value="登录">
    </form>

</body>
//LoginServlet.java中
public class LoginServlet extends HttpServlet{
    public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
        //得到请求参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        //登录 
        UserService service = new  UserService();
        try{
            User user = service.login(username,password);
            if(user != null){
                //登录成功
                //判断是否勾选了自动登录按钮
                String autologin = request.getParameter("autologin");
                if("ok".equals(autologin)){
                    //勾选了
                    //创建一个cookie
                    Cookie cookie = new Cookie(“autologin”,usernaem+"::"+password);
//cookie持久化
                    cookie.setMaxAge(60*60*24*10);//存储10天
                    cookie.setPath("/");
                    response.addCookie(cookie);

                }
                request.getSession().setAttribute("user",user);
                response.senRedirect(request.getContextPath()+"/demo4/success.jsp")
                return;
            }else{
                request.setAttribute("login.message","用户名或密码错误");
                request.getRequestDispatche("/demo4/login.jsp").forward(request,response);
                return;
            }
        }catch(SQLException e){
            e.printStackTrace();
            request.setAttribute("login.message","登录失败");
            request.getRequestDispatche("/demo4/login.jsp").forward(request,response);
            return;
        }
    }
}
public calss AutoLoginFilter implements Filter{
    public void destroy){

    }
    public void doFilter(ServletRequest req,ServletResponse resp,FilterChain chain) throws IOException,ServletException{
        //强制转换
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse)resp;
        //操作
        //得到cookie中的username.password
        Cookie cookie = CookieUtils.findCookidByName(request.getCookies(),"autologin");
        if(cookie != null){
            //找到了,进行自动登录
            String username = cookie.getValue().split("::")[0];
            String password = cookie.getValue().split("::")[1];
            UserService service = new UserService();
            User user = service.login(username,password);
            if(user != null){
                //查找到用户,进行自动登录
                request.getSession().setAttribute("user",user);
            }
        }
        //放行
        chain.doFilter(request,response);
    }
    public void init(FilterConfig filterConfig) throws ServletException{    
    }
}
public calss CookieUtils{
    public static Cookie findCookieByName(Cookie[] cs,String name){
        if(cs==null || cs.length==0){
            return null;
        }
        for(Cookie c : cs){
            fi(c.getName().equals(name)){
                return c;
            }
        }
        return null;
    }
}

问题分析:

  • 如果用户已经登录了,不需要自动登录。
  • 如果用户是进行例如,注册,登录操作,不需要自动登录。
    • 得到请求资源路径,判断是否是登录,注册操作
    • 例如:Http://localhost/day21-2/demo4/login.jsp
    • String uri = request.getRequestURI();—>/day21-2/demo4/login.jsp
    • String contextPath = requext.getContextPath();—>/day21-2
    • String path = uri.subString(contextPath.length());—>/demo4/login.jsp
public void doFilter(ServletRequest req,ServletResponse resp,FilterChain chain) throws IOException,ServletException{
        //强制转换
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse)resp;
        //操作
        //判断用户没有登录,才进行自动登录
        User u= (User)request.getSession().getAttribute("user");
        if(u==null){
                //得到cookie中的username.password
            Cookie cookie = CookieUtils.findCookidByName(request.getCookies(),"autologin");
            if(cookie != null){
            //找到了,进行自动登录
                String username = cookie.getValue().split("::")[0];
                String password = cookie.getValue().split("::")[1];
                UserService service = new UserService();
                User user = service.login(username,password);
                if(user != null){
                //查找到用户,进行自动登录
                    request.getSession().setAttribute("user",user);
                }
            }

        }
        //放行
        chain.doFilter(request,response);
    }

如果用户是中文,怎样处理?

  • cookie中不能存储中文的。
  • 存储时,存utf-8码,使用时解码
  • 存:Cookie cookie = new Cookie(“autologin”,URLEncoder.encode(username,”utf-8”)+”::”+password); (LoginServlet.java)
  • 取:String username = URLDecoder.decode(cookie.getValue().split(“::”)[0],”utf-8”); (AutoLoginFilter)

关于密码的安全性问题

  • 可以对密码进行加密 md5
  • mysql中加密方式:md5(字段)
    • update user set password = md5(password);
  • java中加密方式:
public class Md5Utils{
    public static STring md5(String plainText){
        byte[] secretBytes = null;
        try{
            secretBytes = MessageDigest.getInstance("md5").digest(plainText.getBytes());
        }catch(NoSuchAlgorithmException e){
            throw new RuntimeException("没有md5这个算法!")
        }
        String md5code = new BigInteger(1,sevretBytes).toString(16);
        for(int i=0;i<32-md5code.length();i++){
            md5code = "0"+md5code;
        }
        return md5code; 
    }
}
//UserDao.java
return runner.query(sql,new BeanHandler<User>(User.class),username,Md5Utils.md5(password));

url级别权限控制

  • 权限控制的原理
    • 可以做一个权限的Filter,在Filter中判断用户是否登录了,如果登录了,可以访问资源,如果没有登录,不能访问资源。
  • 问题1:怎样判断哪些资源需要权限,哪些资源不需要权限?
  • String uri = request.getrequestURI();
    String contextPath = request.getContextPath();
    String path = uri.substring(contextPath.length());
    if(path.equals("/book_add") || path.equals("/book_update")||path.equals("/book_delete")||path.equals("/book_search")){
    }
  • 问题2:我们的用户是有role的,如果admin,可以访问所有资源,而User只能访问book_search怎样处理。
if("admin".equals(user.getRole())){
    //这是admin角色
    if(!(path.equals("/book_add")||path.eauals("/book_update")||path.equals("/book_delete"))){
        throw new PrivilegeException();
    }else{
        //这是User角色
        if(!(path.equals("/book_search"))){
            throws new PrivilegeException();
        }
    }
}

对权限控制代码优化

主要对url路径的判断进行优化

  • 在src下创建两个配置文件,user.properties admin.properties
    • 在这两个文件中分别保存不同的角色具有的权限路径
    • 例如:admin.properties中url=/book_add,/book_delete,/book_update,/book_serch
  • 在PrivilegeFilter中完成权限控制
    • 在init方法中将配置文件中的信息读取出来分别保存到users,admins两个List<String>集合中。
      • ResourceBundle.getBundle(String baseName);
      • admin.properties文件它的基名就是admin
      • user.properties文件它的基名就是user.
      • 得到ResourceBundle对象,可以通过它的getString(String name);
      • bundle.getString(“url”);这就得到了配置文件中名称叫url的值。
    • 在判断时就比较方便
      • 判断资源路径是否需要权限控制
        • if(admins.contains(path) || users.contains(path))
    • 判断每一个角色是否可以访问资源路径
      • if(!(admins.contains(path))){
        throw new PrivilegeException();
        }
相关标签: Filter