路漫漫其修远兮,吾将上下而求索|

粤先生

园龄:10个月粉丝:1关注:2

Filter过滤器

Filter过滤器

一、Filter介绍

  • Filter(过滤器)是一种用于对数据流进行处理的软件组件。
  • Filter 的作用是从输入流中获取数据,对其进行处理后再将其写入输出流中。
  • Filter 组件通常用于数据校验、数据转换、数据压缩等方面,以及对网络通信进行处理。
  • 在 Web 开发中,Filter 是 Servlet 标准中的一种组件,用于在 Servlet 执行之前或之后对请求和响应进行处理。可以通过 Filter 实现各种功能,如请求参数过滤、字符编码转换、请求重定向、登录验证等。
  • 使用 Filter 组件可以提高 Web 应用的易用性、安全性和可扩展性。

二、Filter应用场景

(1)Filter(过滤器)是Java Servlet API中一种可以在请求和响应的处理过程中,干预或修改请求和响应的内容的组件。它主要用来过滤HTTP请求和响应,对前端的输入进行过滤,保护系统安全,提高应用的性能等。

(2)Filter应用的场景包括:

  • 参数校验:用户输入的参数可能包含恶意字符或参数格式错误,通过使用Filter可以拦截并进行参数校验,以保证应用安全。
  • 多语言选择:通过获取请求头的语言参数,Filter可以根据用户的语言选择相应的语言。
  • 登录拦截:通过Filter对所有请求进行拦截,检查用户是否登录,若未登录则跳转至登录页面。
  • 编码转换:对于不同的请求和响应,可能需要采用不同的编码方式,Filter可以将请求和响应进行编码转换。
  • 访问控制:通过Filter实现拦截指定路径的请求,实现权限访问控制。

以上是Filter应用的常见场景,它可以通过Java Servlet API提供的Filter接口进行实现。同时,Filter的执行顺序可以通过在web.xml中配置Filter的顺序来决定。

三、Filter拦截器流程图

四、编写Filter过滤器

1. 写一个类实现Filter接口

Filter接口包含三个生命周期方法:void init(FilterConfig)、void doFilter(ServletRequest, ServletResponse, FilterChain)和void destroy()

  • void init(FilterConfig) -- 创建Servlet对象之后马上执行,Filter会在服务器启动时就创建

    public void init(FilterConfig filterConfig) throws ServletException {}
    
  • void doFilter(ServletRequest, ServletResponse, FilterChain) -- 每次过滤都会执行

     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {}
    
  • void destroy() -- 销毁之前执行,在服务器关闭时销毁

     public void destroy() {}
    
  • Filter也是跟Servlet一样是单例的

2. 在web.xml中配置Fliter过滤器

Filter过滤器在web.xml中配置与Servlet差不多,只不过需要注意的是标签下这个标签内是需要拦截的对象,一般都用斜杠 ”/“。而且大多数拦截是这样的/*,这里只是演示拦截AServlet

<web-app ...>  
	<filter>
        <filter-name>xxx</filter-name>
        <filter-class>cn.web.filter.AFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>xxx</filter-name>
        <url-pattern>/AServlet</url-pattern>
    </filter-mapping>
</web-app> 

3. 实现Fliter过滤器案例

  • 创建一个AServlet类和一个AFilter类
  • 在web.xml中配置Fliter
  • 将项目放到Tomcat,启动服务器

AServlet.java

public class AServlet extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("AServlet....");
    }
}

AFilter.java

public class AFilter implements Filter {
    /**
     * 创建之后马上执行,用作初始化
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("创建拦截器!!!");
    }

    /**
     * 每次过滤时都执行
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("正在拦截!!!");
        filterChain.doFilter(servletRequest,servletResponse); // 放行!
        System.out.println("你又回来了?");
    }

    /**
     * 销毁之前执行,用来对非内存资源进行释放
     */
    @Override
    public void destroy() {
        System.out.println("拦截器已销毁!!!");
    }
}

web.xml中配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>AServlet</servlet-name>
        <servlet-class>cn.web.servlet.AServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>AServlet</servlet-name>
        <url-pattern>/AServlet</url-pattern>
    </servlet-mapping>

    <filter>
        <filter-name>AFilter</filter-name>
        <filter-class>cn.web.filter.AFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AFilter</filter-name>
        <url-pattern>/AServlet</url-pattern>
    </filter-mapping>
</web-app>

五、FilterConfig接口

1. FilterConfig接口介绍

FilterConfig 是 Servlet API 提供的一个用于获取 Filter 程序在 web.xml 文件中的配置信息的接口,该接口封装了 Filter 程序在 web.xml 中的所有注册信息,并且提供了一系列获取这些配置信息的方法。

方法 功能
String getInitParameter(String name) getInitParameter(String name) 方法用于返回在 web.xml 文件中为 Filter 所设置的某个名称的初始化参数值,如果指定名称的初始化参数不存在,则返回 null
String getFilterName() getFilterName() 方法用于返回在 web.xml 文件中为 Filter 所设置的名称,也就是返回 <filter-name> 元素的设置值
ServletContext getServletContext() getServletContext()方法用于返回 FilterConfig 对象中所包装的 ServletContext 对象的引用
Enumeration getInitParameterNames() getInitParameterNames() 方法用于返回一个 Enumeration 集合对象,该集合对象包含在 web.xml 文件中为当前 Filter 设置的所有初始化参数的名称

六、FilterChain(过滤器链)

1. FilterChain介绍

在 Web 应用中,可以部署多个 Filter,若这些 Filter 都拦截同一目标资源,则它们就组成了一个 Filter 链(也称过滤器链)。过滤器链中的每个过滤器负责特定的操作和任务,客户端的请求在这些过滤器之间传递,直到传递给目标资源。

2. FilterChain 接口

javax.servlet 包中提供了一个 FilterChain 接口,该接口由容器实现。容器将其实例对象作为参数传入 Filter 对象的 doFilter() 方法中。Filter 对象可以使用 FilterChain 对象调用链中下一个 Filter 的 doFilter() 方法,若该 Filter 是链中最后一个过滤器,则调用目标资源的 service() 方法。

总结作用:让 Filter 链上的当前过滤器放行,使请求进入下一个 Filter。

方法 功能
void doFilter(ServletRequest, ServletResponse) 使用该方法可以调用过滤器链中的下一个 Filter 的 doFilter() 方法,若该 Filter 是链中最后一个过滤器,则调用目标资源的 service() 方法。

3. Filter 链的拦截过程

img

当浏览器访问 Web 服务器中的资源时,需要经过两个过滤器 Filter1 和 Filter2。首先 Filter1 会对这个请求进行拦截,在 Filter1 中理完请求后,通过调用 Filter1 的 doFilter() 方法将请求传递给 Filter2,Filter2 处理用户请求后同样调用 doFilter() 方法,最终将请求发送给目标资源。当 Web 服务器对这个请求做出响应时,也会被过滤器拦截,但这个拦截顺序与之前相反,最终将响应结果发送给客户端浏览器。

注:过滤器链中的任何一个 Filter 没有调用 FilterChain.doFilter() 方法,请求都不会到达目标资源。

总结:执行目标资源,或是执行下一个过滤器!如果没有下一个过滤器那么执行的是目标资源,如果有,那么就执行下一个过滤器!

4. Filter 链中拦截器的执行顺序

通过 web.xml 配置的 Filter 过滤器,执行顺序由 <filter-mapping>标签的配置顺序决定。**<filter-mapping> 靠前,则 Filter 先执行,靠后则后执行。通过修改 <filter-mapping> 的顺序便可以修改 Filter 的执行顺序。

注:通过 @WebFilter 注解配置的 Filter 过滤器,无法进行排序。

5.演示多个Filter拦截器执行顺序

  • 创建两个Servlet类和两个Filter类
  • 在web.xml中配置Servlet和Filter
  • 加载到Tomcat,启动服务器

实现AServlet和BServlet类文件

AServlet.java

public class AServlet extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("AServlet已开始...");
    }
}

BServlet.java

public class BServlet extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("BServlet已开始...");
    }
}

实现AFilter和BFilter接口文件

AFilter.java

public class AFilter implements Filter {

    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("创建A拦截器!!!");
    }

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("AFilter已开始...");
        filterChain.doFilter(servletRequest,servletResponse); // 放行!
        System.out.println("AFilter已结束...");
    }

    public void destroy() {
        System.out.println("A拦截器已销毁!!!");
    }

}

BFilter.java

public class BFilter implements Filter {

    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("创建B拦截器!!!");
    }

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("BFilter已开始...");
        filterChain.doFilter(servletRequest,servletResponse); // 放行!
        System.out.println("BFilter已结束...");
    }

    public void destroy() {
        System.out.println("B拦截器已销毁!!!");
    }
}

在web.xml中配置Servlet和Filter

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>AServlet</servlet-name>
        <servlet-class>cn.web.servlet.AServlet</servlet-class>
    </servlet>
    <servlet>
        <servlet-name>BServlet</servlet-name>
        <servlet-class>cn.web.servlet.BServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>AServlet</servlet-name>
        <url-pattern>/AServlet</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>BServlet</servlet-name>
        <url-pattern>/BServlet</url-pattern>
    </servlet-mapping>


    <filter>
        <filter-name>AFilter</filter-name>
        <filter-class>cn.web.filter.AFilter</filter-class>
    </filter>
    <filter>
        <filter-name>BFilter</filter-name>
        <filter-class>cn.web.filter.BFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>BFilter</filter-name>
        <url-pattern>/BServlet</url-pattern>
    </filter-mapping>
</web-app>

因为主要拦截的是BServlet,访问http://localhost:8080/Filter/BServlet后的执行结果

创建B拦截器!!!
创建A拦截器!!!

AFilter已开始...
AFilter已结束...

AFilter已开始...
BFilter已开始...
BServlet已开始...
BFilter已结束...
AFilter已结束...

B拦截器已销毁!!!
A拦截器已销毁!!!

七、Filter的四种拦截方式

1. 四种拦截方式

都必须在<filter-mapping>标签下进行四种拦截配置。

  • 拦截请求(REQUEST)-- <dispatcher>REQUEST</dispatcher>
  • 拦截转发(FORWARD)-- <dispatcher>FORWARD</dispatcher>
  • 拦截包含(INCLUDE)-- <dispatcher>INCLUDE</dispatcher>
  • 拦截错误(ERROR)-- <dispatcher>ERROR</dispatcher>

默认拦截方式为REQUEST,没有配置的话拦截方式就是REQUEST

2.演示

现在配置一个FORWARD转发拦截方式并通过访问BServlet转发至AServlet

AServlet.java

public class AServlet extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("AServlet已开始...");
    }
}

BServlet.java

public class BServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /**
         * getRequestDispatcher()是请求转发
         * forward(req,resp)是转发(容器中控制权转向),在客户端浏览器地址栏中不会显示出转向后的地址。
         */
        req.getRequestDispatcher("/AServlet").forward(req,resp);
    }
}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    id="WebApp_ID" version="4.0">
    <display-name>day0412_1</display-name>
    <filter>
        <filter-name>AFilter</filter-name>    <!-- 自定义名称 -->
        <filter-class>cn.web.filter.AFilter</filter-class> <!-- Filter类所在路径 -->
    </filter>
    <filter-mapping>
        <filter-name>AFilter</filter-name> <!-- 自定义名称匹配 -->
        <url-pattern>/*</url-pattern> <!-- 拦截的资源路径 -->
    </filter-mapping>
    <filter>
        <filter-name>BFilter</filter-name>    <!-- 自定义名称 -->
        <filter-class>cn.web.filter.BFilter</filter-class> <!-- Filter类所在路径 -->
    </filter>
    <filter-mapping>
        <filter-name>BFilter</filter-name> <!-- 自定义名称匹配 -->
        <url-pattern>/AServlet</url-pattern> <!-- 拦截的资源路径 -->
        <dispatcher>FORWARD</dispatcher>
    </filter-mapping>
</web-app>

启动服务器,浏览器访问BServlet:http://localhost:8080/Filter/BServlet

查看控制台输出结果:image-20240407172116759

结论分析:

​ 首先是AFilter拦截成功,因为AFilter拦截方式为默认的REQUEST请求,而访问BServlet正是这种方式;然后是AFilter放行,放行后执行BServlet,执行BServlet时会执行BServlet中的转发操作,那么这个时候就会被BFilter拦截,因为BFilter的拦截方式是FORWARD转发,随后BFilter会进行放行,然后执行AServlet。

可以发现执行顺序就是先执行的后结束

八、设置目标资源方式

必须在web.xml文件下的<filter-mapping>标签下进行设置

<filter-mapping>标签包含以下元素:

  • <filter-name> -- 拦截器名字
  • <url-pattern> -- 拦截的资源路径
  • <dispatcher> -- 拦截方式
  • <servlet-name> -- 它与某个Servlet的配置名称相同

九、Filter小结

1. Filter的三个方法

  • void init(Eilterconfig):在Tomcat启动时被调用;
  • void destroy():在Tomcat关闭时被调用;
  • void doFiter(ServletRequest, ServletResponse, FilterChain):每次有请求时都调用该方法;

2. FilterConfig接口

与ServletConfig相似,用来获取Filter的初始化参数

  • ServletContext getServletContext():获取ServletContext的方法;
  • String getFilterName():获取Filter的配置名称;
  • String getlnitParameter(String name):获取 Filter的初始化配置,与元素对应;
  • Enumeration getlnitParameterMame():获取所有初始化参数的名称。

3. FilterChain接口

  • void doFilter( ServletRequest,ServletResponse):放行!

  • 表示执行下一个过滤器,或者执行目标资源。

  • 可以在调用FilterChain的doFilter()方法的前后添加语句,在FilterChain的 doEilter()方法之前的语句会在目标资源执行之前执行,在FilterChain的 doFilter()方法之后的语句会在目标资源执行之后执行。

4. 四种拦截方法

四条拦截方式:REQUEST、FORWARD、INCLUDE、ERROR,默认是REQUEST方式。

  • REQUEST:拦截直接请求方式;
  • FORWARD:拦截请求转发方式;
  • INCLUDE:拦截请求包含方式;
  • ERROR:拦截错误转发方式。

十、过滤器应用案例

案例1:分IP统计访问次数

  • 说明

    • 网站统计每个IP地址访问本网站的次数。
  • 分析

    • 用什么来统计网站的访问次数?
      因为网站可能有多个页面,无论哪个页面被访问,都要统计访问次数,也就是说统计工作在任何的资源被访问之前都要执行,所以使用过滤器比较方便。而且我们的这个过滤器不需要任何的拦截操作,只需要统计次数就可以了。
    • 用什么来存储网站中每个IP的访问次数?
      我们需要用什么来装载统计数据呢?答案是Map<String,Integer>,我们可以在过滤器中创建一个Map,key就是IP地址,value就是对应IP地址访问的次数。
      当有用户访问时,就获取请求的IP地址:如果这个IP在map中存在,就说明以前访问过,就在访问次数上+1;如果IP在map中不存在,就设置访问次数为1。
    • Map怎么创建?存放在哪里?
      这个Map什么时候创建呢,我们可以使用监听器,也就是使用ServletContextListener,在服务器启动的时候完成创建,并且存放到ServletContext中。
  • 代码步骤

    • 创建访问页面

      • 新建两个jsp,供访问使用。image-20240410132755513
    • 创建监听器

      • 创建一个监听器,让其在服务器启动的时候创建一个Map,并且保存到ServletContext中。

        public class AListener implements ServletContextListener {
            /**
             * 在服务器启动时创建Map,保存到ServletContext
             */
            public void contextInitialized(ServletContextEvent sce) {
                // 创建 Map
                Map<String, Integer> map = new LinkedHashMap<String,Integer>();
                // 得到ServletContext对象
                ServletContext application = sce.getServletContext();
                // 把 map 保存到 application对象中
                application.setAttribute("map",map);
            }
        
            public void contextDestroyed(ServletContextEvent sce) {}
        }
        
    • 创建过滤器

      • /**
         * 从application中获取Map
         * 从request中得到当前客户端的 IP
         * 进行统计工作,结果保存到Map中
         */
        public class AFilter implements Filter {
            private FilterConfig config = null;
            // 在服务器启动时就会执行本方法,而且本方法只会执行一次!
            public void init(FilterConfig config) throws ServletException {
                // 保存config
                this.config = config;
            }
        
            public void destroy() {}
        
            public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                /**
                 * 1.得到application中的map
                 * 2.从request中获取当前客户端的IP地址
                 * 3.查看map中是否存在这个ip对应访问次数,如果存在,把次数+1再保存回去
                 * 4.如果不存在这个ip,那么说明第一次访问本站,设置访问次数为1
                 */
                // 得到application
                ServletContext app = config.getServletContext();
                // 得到application中的map
                Map<String, Integer> map = (Map<String, Integer>) app.getAttribute("map");
                // 从request中获取当前客户端的IP地址
                String ip = servletRequest.getRemoteAddr();
                // 判断map中是否存在该IP地址
                if (map.containsKey(ip)){ // ip在map中存在,说明不是第一次访问
                    int cnt = map.get(ip);
                    map.put(ip, cnt+1);
                }else { // ip在map中不存在,说明是第一次访问
                    map.put(ip, 1);
                }
                // 把map再保存回application中
                app.setAttribute("map", map);
        
                filterChain.doFilter(servletRequest,servletResponse); // 肯定放行
            }
        }
        
    • web.xml的有关配置

      <filter>
          <filter-name>AFilter</filter-name>
          <filter-class>cn.web.filter.AFilter</filter-class>
      </filter>
      <filter-mapping>
          <filter-name>AFilter</filter-name>
          <url-pattern>/*</url-pattern>
      </filter-mapping>
      <listener>
          <listener-class>cn.web.listener.AListener</listener-class>
      </listener>
      
    • 创建一个显示页面

      • 创建一个show.jsp,用来显示我们的统计数据。

        <body>
            <h1 align="center">显示结果</h1>
            <table align="center" width="60%" border="1">
                <tr>
                    <th>IP地址</th>
                    <th>访问次数</th>
                </tr>
                <c:forEach items="${applicationScope.map }" var="entry">
                    <tr>
                        <td>${entry.key }</td>
                        <td>${entry.value }</td>
                    </tr>
                </c:forEach>
            </table>
        </body>
        
    • 运行方式

案例2:粗粒度权限管理

1、粗粒度权限控制(拦截是否登录、拦截用户名admin权限)

RBAC→基于角色的权限控制

  • tb_user
  • tb_role
  • tb_userrole
  • tb_menu(增、删、改、查)->属于细粒度权限控制
  • tb_rolemenu

2、说明

我们给出三个页面: index.jsp、 userjsp、 admin.jsp

  • index.jsp:谁都可以访问,没有限制;
  • user.jsp:只有登录用户才能访问;
  • admin.jsp:只有管理员才能访问。

3、分析

  • 首先需要一个登录页面(login.jsp)用于传递登录的用户名和密码。
  • 然后创建一个Servlet,当用户登录成功后,进行判断权限级别,并把用户名保存到session域中。
  • 最后创建拦截器,两种过滤方式:UserFilter、AdminFilter。
JSP的编写

创建四个JSP文件,分别为:welcom.jsplogin.jspusers文件夹下的user.jspadmin文件夹下的admin.jsp

welcom.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
  <title>Welcome</title>
</head>
<body>
<h1>游客界面</h1>
当前用户:<%=session.getAttribute("username")%><br/>
<a href="<c:url value="/welcome.jsp"/>">游客入口</a><br>
<a href="<c:url value="/users/users.jsp"/>">用户入口</a><br>
<a href="<c:url value="/admin/admin.jsp"/>">管理员入口</a><br>
</body>
</html>

login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Login</title>
</head>
<body>
<h1>登录</h1>
<font color="red"> ${msg} </font><br/>
当前用户:<%=session.getAttribute("username")%><br/>
<form action="<c:url value="/Login"/>" method="post">
    <table>
        <tr>
            <td>用户名:</td>
            <td><input type="text" name="username"/></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="password" name="password"/></td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" value="登录"/></td>
        </tr>
    </table>
</form>
</body>
</html>

user.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Welcome</title>
</head>
<body>
<h1>用户界面</h1>
当前用户:<%=session.getAttribute("username")%><br/>
<a href="<c:url value="/welcome.jsp"/>">游客入口</a><br>
<a href="<c:url value="/users/users.jsp"/>">用户入口</a><br>
<a href="<c:url value="/admin/admin.jsp"/>">管理员入口</a><br>
</body>
</html>

admin.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Welcome</title>
</head>
<body>
<h1>管理员界面</h1>
<%
    String name= (String) session.getAttribute("admin");
    session.setAttribute("username",name);
%>
当前用户:<%=session.getAttribute("username")%><br/>
<a href="<c:url value="/welcome.jsp"/>">游客入口</a><br>
<a href="<c:url value="/users/users.jsp"/>">用户入口</a><br>
<a href="<c:url value="/admin/admin.jsp"/>">管理员入口</a><br>
</body>
</html>
编一个User对象

User.class

package cn.web.user;

public class User {
    private String username;
    private String password;
    private int grade;

    public User(String username, String password, int grade) {
        this.username = username;
        this.password = password;
        this.grade = grade;
    }

    public int getGrade() {
        return grade;
    }

    public void setGrade(int grade) {
        this.grade = grade;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "web.user.User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
Servlet的编写

创建servlet文件夹存放LoginServlet.class用于处理请求。

LoginServlet.class

@WebServlet(name = "LoginServlet")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");  //处理中文问题

        /*1.获取用户名,获取密码
        * 2.判断用户名中和密码是否为“”,
        *   3.如果不为空,且包含特定字符,就是管理员
        *   4.如果不包含,就是普通用户
        *   5.否则就是游客
        * 6.把登录的用户名称保存到session中
        * 7.转发到welcome.jsp*/
        String name=request.getParameter("username");
        String password=request.getParameter("password");

        if (name!="" && password!=""){
            if (name.contains("chen") && password.equals("123456")){
                request.getSession().setAttribute("admin",name);
                //name是login.jsp中提交的值,一个键值对:username:chen
                //setAttribute,创建一个新的session域对象,名称为andmin,值为name的值
            }else {
                request.getSession().setAttribute("username",name);
            }
        }else {
            request.getSession().setAttribute("tourist",name);
        }

        request.getRequestDispatcher("/UserDemo/welcom.jsp").forward(request,response);
        System.out.println("已经转发");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

在web.xml配置LoginServlet路径

<servlet>
    <servlet-name>Login</servlet-name>
    <servlet-class>cn.web.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>Login</servlet-name>
    <url-pattern>/Login</url-pattern>
</servlet-mapping>

此时Servlet与JSP完成,可以访问三个页面畅通无阻。而为了实现我们的粗粒度权限管理,我们还需要编写两个过滤器,以达到权限管理的目的。

Filter的编写

创建filter文件夹存放UserFilter.class和AdminFilter用于过滤,以达到权限管理的目的。

UserFilter.class

public class UserFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        /*1.得到session
         * 2.判断session域中是否存在admin,如果存在放行
         * 3.判断session域中是否存在username,如果存在放行
         * 4.否则打回login.jsp*/
        HttpServletRequest request= (HttpServletRequest) req; //强转,用它才能使用getSession()方法
        String name = (String) request.getSession().getAttribute("admin");
        if (name!=null){
            /*获取admin,如果不为空,就是管理员*/
            chain.doFilter(req, resp);
            return;
        }
        name=(String)request.getSession().getAttribute("username"); //获取用户
        if (name!=null){
            chain.doFilter(req,resp);
        }else {
            request.setAttribute("msg","游客请登录");    //保存错误信息到request域中,再转发
            request.getRequestDispatcher("/login.jsp").forward(req,resp);
            System.out.println("UserFilter拦截");
        }
    }

    public void init(FilterConfig config) throws ServletException {

    }

}

AdminFilter.class

public class AdminFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest request= (HttpServletRequest) req;
        String name= (String) request.getSession().getAttribute("admin");
        if (name!=null){
            /*获取admin,如果不为空,就是管理员*/
            chain.doFilter(req, resp);
            return;
        }else {
            request.setAttribute("msg","没有管理员权限");  //保存错误信息到request域中,再转发
            request.getRequestDispatcher("/login.jsp").forward(req,resp);
            System.out.println("AdminFilter拦截");
        }
    }

    public void init(FilterConfig config) throws ServletException {

    }

}

在web.xml中进行UserFilter、AdminFilter的配置

    <filter>
        <filter-name>UserFilter</filter-name>
        <filter-class>cn.web.filter.UserFilter</filter-class>
    </filter>
    <filter>
        <filter-name>AdminFilter</filter-name>
        <filter-class>cn.web.filter.AdminFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>UserFilter</filter-name>
        <url-pattern>/user/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>AdminFilter</filter-name>
        <url-pattern>/admin/*</url-pattern>
    </filter-mapping>

分析与总结

流程分析

  • login.jsp提交信息到LoginServlet;
  • LoginServlet判断提交信息中是否包含指定数据,如果包含管理员数据则在session域中创建名为admin的数据,并将提交信息中的username的值作为新值传递给admin;若满足用户判读条件,则仿照上面的创建一个名为username的键值对。最后将其转发到welcom.jsp;
  • welcom.jsp中定义三个超链接,指向welcom.jsp和user文件夹下的user.jsp以及admin文件夹下的admin.jsp
  • 在welcom.jsp中,UserFilter和AdminFilter的作用就显示出来了。
  • UserFilter和AdminFilter通过session域中是否存在username和admin的数据,判断是否放行。

其中的知识点有JavaWeb三大组件的Servlet、Filter和Session域。

本文作者:粤先生

本文链接:https://www.cnblogs.com/magicYue/p/18174184

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   粤先生  阅读(69)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起