当前位置: 首页 > news >正文

[Servlet 3]会话管理、进阶API、监听过滤器

进入到Servlet3的部分,这里主要以会话管理,Servlet进阶API,监听器与过滤器等相应的内容。会话管理是对于SessionID,Cookie的管理操作。进阶API主要是ServletConfig,ServletContext为例。监听与过滤器主要是对于请求进行的处理。

Servlet1知识点
Servlet2知识点

等Servlet 4(主要内容是异步处理,数据库开发)上传后将以一个项目为实例进行进阶讲解,以JSP+Servlet项目结束后将紧接着SpringBoot来看了。关于标签页等前端内容将不再进行系统讲解,后期将以Bootstrap,Vue配合Django项目为例。

这里多说几句,对于Servlet都是以几个模块为例的,web开发主要的还是以项目为主,主要可以使用的开源平台例如Gitee,Github进行开发。

目录

  • 会话管理
    • 会话管理的基本原理
      • 使用隐藏域
      • 使用Cookie
      • 使用URL重写
    • HttpSession会话管理
      • 使用HttpSession管理会话
      • HttpSession 与URL 重写
      • HttpSession 生命周期
    • HttpSession会话管理的实例
  • Servlet 进阶API
    • Servlet ,ServletConfig, GenericServlet
    • 使用ServletConfig
    • 使用ServletContext
  • 监听器
    • ServletContext事件
    • HttpSession 事件监听器
      • HttpSessionIDListener :
      • HttpSessionAttributeListener
      • HttpSessionBindingListener
  • 过滤器
    • 实现与设置过滤器
      • Filter接口

会话管理

会话管理的基本原理

会话管理,通过以下来实现:使用隐藏域、使用Cookie、使用URL重写。

使用隐藏域

隐藏域是将表单内容在显示时隐藏,即不显示数据。在JSP将input标签的type设定为hidden,即生成一个隐藏表单域。再将会话的唯一标识记录到隐藏域中的value属性,并设定name属性。表单提交时,会话标识也被提交到服务端了,根据它找到对应的会话对象。
使用隐藏域,需要每个页面包含标识表单,这样才能跳转时候保存会话并进行管理。但是安全性差,目前在开发中很少使用。

使用Cookie

利用Cookie实现会话管理是最容易的实现方式,也是使用较多的方法。原理是服务端保存的会话对象中设定会话的唯一标识,客户端将会话标识保存在Cookie.当发起请求,从Cookie中取得会话标识并发送给服务器,服务器在收到请求后,根据发送的会话标识查找会话对象。
目前这个是主流方法。

在这里插入图片描述

使用URL重写

URL重写是指在URL地址末尾添加会话标识,改写原先的URL.本质是用于唯一标识会话的信息以参数的形式添加到URL中,服务端收到请求时,解析标识,利用会话标识查找对象。该方法是用在浏览器Cookie被禁用的情况下,利用该方法实现会话跟踪而不受到参数影响。
问题就是,复制URL会暴露会话标识,信息不安全。

无论使用哪种方式实现会话管理,在服务端需要完成相关代码才能完整实现会话管理的任务。

HttpSession会话管理

HTTP协议是无意识单向(服务端不能主动连接)协议,只能等待并答复客户端请求。所以使用session来保存客户端信息。

使用HttpSession管理会话

当用户第一次请求服务器时,就创建了session对象。以key-value形式保存,通过读写方法保存客户的状态信息,例如getAttribute(String key) 用于获得参数,setAttribute(String key,Object value)用于设定参数和参数值。

import javax.servlet.http.HttpSession
HttpSession session =request.getSession() //获取session对象
Session.setAttribute("username","Joah") //设置session属性

JSP内置了Session对象,可以直接使用,Servlet使用上述方法获取session对象。若JSP设定了page session=“false” 代表不可用。
login.jsp

<%
	String path = request.getContextPath();
%>
<body>
    <form action="<%=path%>/servlet/CheckUser" method = "post">
        用户名: <input name="userID"/>
        密码: <input name="passwd" type="password"/>
        <input type="submit" value="提交"/>
        <input type="reset" value="重置"/>
    </form>
</body>

使用Servlet 实现,编写跳转的Servlet类CheckUser

// CheckUser.java
do Post(...)...{
    request.setCharacterEncoding("UTF-8");
    String userID = request.getParameter("userID");
    String passwd = request.getParameter("passwd");
    
    //判断是否为lin1用户且密码相符
    if(userID!=null && "lin1".equals(userID) && passwd!=null && "123456".equals(passwd)) {
        //获得session对象
        HttpSession session = request.getSession();
        //设置user参数
        session.setAttribute("user",userID);
        //跳转页面
        RequestDispatcher dispatcher =request.getRequestDispatcher("/welcome.jsp");
        dispatcher.forward(request,response);
    }else {
        RequestDispatcher dispatcher = request.getRequestDispatcher("/login.jsp");
        dispatcher.forward(request,response);
    }
}

// welcome.jsp 读取session对象ID
<%
	String user = (String)session.getAttribute("user");
	if(user == null ) {
%>
<jsp:forword page = "/login.jsp"/>
 <%   } %>
<body>
    welcome : <%=user%>
</body>

HttpSession管理会话的原理:利用服务器来管理会话的机制,当程序客户端创建session,服务器会检查是否包含一个session标识。如果有服务器就把session检索使用。

HttpSession 与URL 重写

URL重写在客户端不支持Cookie情况下,实现方式将sessionID 添加到URL.

response.encodeURL("login.jsp");
或者
response.sendRedirect(response.encodeRedirectURL("login.jsp"));

//方法的效果一样,如果支持Cookie那么URL不变,不支持URL会带有jsessionid字符串的地址

HttpSession 生命周期

Servlet 有它的的生命周期,同样,HttpSession也有生命周期:创建 - 使用 - 消亡
创建

request.getSession() 或者 request.getSession(true) 

使用
将sessionID 存入Cookie中。客户端发送请求时候会将sessionID与request一起传送给服务器端;根据请求过来的sessionID与保存在服务器端的session 对应起来,判断是否为同一session。
消亡
将浏览器关闭;HttpSession.invalidate() ;session超时
只有访问JSP,Servlet才会创建session,访问静态时候不会创建session对象的。

HttpSession会话管理的实例

通过在线猜数字来说明session会话管理的过程。
guessNumber.jsp

<%
	String flag = request.getParameter("flag");
	String message = "";
	if(flag != null && "larger".equals(flag)) {
        message = "太大了";
    }else if (flag != null && "lessner".equals(flag)) {
        message = "太小了";
    }else if (flag != null && "success".equals(flag)) {
        message = "猜对了";
    }
%>

<body>
    <form action="<%=path%>/servlet/Guess" method ="post" >
        数字: <input name = "guessNumber"/>
        <input type ="sumbit" value="提交" />
        <%=message%>
    </form>
</body>

Servlet : Guess.java

doPost(...) ... {
    String guessNumber = request.getParameter("guessNumber");
    int number = Integer.parseInt(guessNumber);
    //产生一个Session ,并获取session的currentNumeber
    
    HttpSession session = request.getSession();
    Integer currentNumber = (Integer) session.getAttribute("currentNumber");
    String context = request.getContextPath();
    if(currentNumber == null ) {
        //产生1-50的随机数
        currentNumber = 1+(int)(Math.random()*50);
        session.setAttribute("currentNumber",currentNumber);
    }
    // 判断所猜数与cur大小
    if(number > currentNumber) {
        response.sendRedirect(context+"/guessNumber.jsp?flag=larger");
    }else if(number < currentNumber) {
        response.sendRedirect(context+"/guessNumber.jsp?flag=lessner");
    }else {
        currentNumber = 1 + (int)(Math.random()*50);
        session.setAttribute("currentNumber",currentNumber);
        response.sendRedirect(context+"/guessNumber.jsp?flag=success");
    }
}

Servlet 进阶API

编写一个Servlet 类,需要在web.xml 或者通过注解方式配置,这样web容器才能读取Servlet信息。对于每个Servlet配置,web都会生成与之对应的ServletConfig对象,可以得到Servlet初始化参数。

Servlet ,ServletConfig, GenericServlet

GenericServlet 实现了上面两个类的接口,这个使得编写Servlet更加方便,提供了一个简单的方法执行Servlet生命周期。

public abstract class GenericServlet implements Servlet,ServletConfig,java.io.Serializable{
    public GenericServlet() {
        
    }
    public void destory() {
        
    }
    //获得初始化参数方法
    public String getInitParameter(String name) {
        
    }
    //获得参数名称,返回枚举类型
    public Enumeration<String> getInitParameterNames() {
        
    }
    //获得ServletConfig对象
    public ServletConfig getServletConfig() {
        return config;
    }
    //获得ServletContext对象
    public ServletContext getServletContext() {
        
    }
    ....
}

GenericServlet类将ServletConfig封装了。

使用ServletConfig

容器初始化,会为Servlet创建唯一的ServletConfig对象。读取web.xml,将初始化传给ServletConfig,而ServletConfig作为参数传递到init()
Servlet 3.0开始,允许以注入的方式配置Servlet.

@WebServlet(
	name = "ServletConfigDemo",
    ulrPatterns = {"/servletConfigDemo.do"},
)
@WebServlet属性名描述
nameservlet-name
urlPatternsURL匹配模式,url-pattern
initParamsServlet初始化,init-param

编写ServletConfigDemo类,输出初始化参数,成功登录

@WebServlet(
        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取ServletConfig对象
        ServletConfig config = getServletConfig();

        //getInitParameter(name)
        String success = config.getInitParameter("success");
        String error = config.getInitParameter("error");
        System.out.println("success----"+success);
        System.out.println("error----"+error);

        //getInitParameterNames
        Enumeration enumeration = config.getInitParameterNames();
        while(enumeration.hasMoreElements()) {
            String name =(String) enumeration.nextElement();
            String value = config.getInitParameter(name);
            System.out.println("name---"+name);
            System.out.println("value---"+value);
        }

        //getServletContext
        ServletContext servletContext = config.getServletContext();
        System.out.println("servletContext-----"+servletContext);

        //getServletName
        String servletName = config.getServletName();
        System.out.println("servletName---"+servletName);

        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        String userID = request.getParameter("userID");
        String passwd = request.getParameter("passwd");

        //判断是否为lin1
      
    }
)

使用ServletContext

是Servlet的全局存储信息。服务器启动时,Web容器为Web创建唯一的ServletContext对象。其接口定义了运行Servlet应用程序的环境信息,可以获取请求资源的URL,设置等属性。

方法描述
getRealPath(String path)真实路径名
getResource(String uripath)返回由path对应的url对象
getServletInfo获取服务器名字

监听器

监视行为。监听事件来监听请求中的行为而创建的一组类。实现相应的监听接口。触发监听事件,应用服务器会自动调用监听方法。

ServletContext事件

ServletContextListener:生命周期监听器,用来监听web初始化或者结束时响应的动作事件。
方法是contextInitialized (加载和初始化) ,contextDestroyed (应用即将关闭)
实现ServletContextListener步骤如下:
配置:

<listener>
	<listener-class>
    	MyServletContextListener
    </listener-class>
</listener>

// 或者利用注入的方式注入监听类
@WebListener
@WebListener
public class MyServletContextListener implements ServletContextListener {
    Logger log =Logger.getLogger("MyServletContextListener");
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        //获得对象
        ServletContext context = servletContextEvent.getServletContext();
        String name = context.getInitParameter("username");
        System.out.println(name+"实现接口");
        log.debug("初始化name"+name);
        log.debug("Tomcat启动中"); //使用的是log4j 1.2 jar
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        log.debug("Tomcat关闭");
    }
}

web.xml

<context-param>
        <param-name>username</param-name>
        <param-value>John</param-value>
    </context-param>

    <listener>
        <listener-class>MyServletContextListener</listener-class>
    </listener>

HttpSession 事件监听器

HttpSessionIDListener :

监听session ID 的变化

public void sessinIDChanged (HttpSessionEvent se,java.lang.String oldSessionID)

用于通知sessionID发生了变化。实现方法;

@WebListener
public class MyHttpSessionIDListener implements HttpSessionListener {
    
}

//web.xml配置,同上面

下面实现统计在线人数的功能,LoginServlet.java

@WebServlet(
        name = "loginServlet",
        urlPatterns = {"/login.htm"},
        initParams = {
                @WebInitParam(name = "success",value="success.jsp")
        }
)
public class LoginServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public void init() throws ServletException{
        System.out.println("初始化 init loginServlet 方法");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 设置响应内容类型
        doPost(request, response);
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String userName = request.getParameter("username");
        String passwd = request.getParameter("passwd");

        if(checkLogin(userName,passwd)) {
            request.getSession().setAttribute("user",userName);
            request.getSession().setAttribute("count",MyHttpSessionListener.getCount());
           
            System.out.println(userName+" "+passwd);
        }else {
            response.sendRedirect("login.jsp");
        }
        String suss= getInitParameter("success");
        System.out.println(suss);
        response.sendRedirect(suss);

    }

    public  boolean checkLogin(String username,String passwd) {
        HelloUser user = HelloUser.getInstance();
        Map<String,String> map = user.getUserMap();
        if(username!=null && !"".equals(username)&& passwd!=null && !"".equals(passwd)) {
            String[] arr = map.get(username).split("##");
            if(arr[0].equals(passwd)) return true;
            else return  false;
        }else {
            return  false;
        }
    }
}

success.jsp

<p>
    ${sessionScope.count}    ${sessionScope.user}
</p>

关于login.jsp就不再书写。

HttpSessionAttributeListener

属性改变监听器。

接口3个监听方法:
default void attributeAdded(HttpSessionBindingEvent se) :方法用于通知监听器
default void attributeRemoved(...)
default void attributeReplaced(...)

HttpSessionBindingListener

对象绑定监听器,用来监听属性与通知。

default void valueBound(...)
default void valueUnbound(...)

过滤器

作用是阻挡某些事情的发生。在服务器端与客户端起到了一个中间组件的作用,对二者之间的数据进行过滤。
利用过滤器实现如下功能:对用户请求进行身份认证;数据过滤与替换;加密与压缩;审核等

实现与设置过滤器

在Servlet实现过滤器,必须实现Filter接口,并用注入的方式或者在web.xml定义过滤器。

Filter接口

接口方法:

default void init(FilterConfig conf):初始化过滤器
public void doFilter(ServletRequest req,ServletResponse res,FilterChain chain):
default void destroy()
//注入方式
@WebFilter(
	filterName = "myfilter",
    servletNames = {"*.do"},
    urlPatterns = {"/*"},
    initParams = {
        @WebInitParam(name = "param" , value = "paramvalue")
    },
    dispatcherTypes = {DispatcherType.REQUEST}
)

<filter>
	<description>demo</description>
    <filter-name>myfilter</filter-name>
    <filter-class>Myfilter</filter-class>
    
    <init-param>
    	<param-name>param</param-name>
        <param-value>paramvalue</param-value>
    </init-param>
</filter>

<!--过滤器映射配置-->
<filter-mapping>
	<filter-name>myfilter</filter-name>
    <servlet-name>*.do</servlet-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>

请求封装器

将请求内容进行修改,例如修改请求字符编码、替换字符、权限验证等。

// EncodingFilter.java
@WebFilter(
	filterName = "encodingFilter",
    urlPatterns ={"/*"},
    initParams ={
        @WebInitParam(name ="ENCODING",value ="UTF-8")
    }
)

public class EncodingFilter implements Filter{
    public void init(FilterConfig filterConfig) throws ServletException {
        encoding = filterConfig.getInitParameter("ENCODING");
        filterName = filterConfig.getFilterName();
        log.debug("编码");
    }
    public void doFilter(...)... {
        request.setCharacterEncoding(encoding); //只对消息体中的数据起作用,对URL不起作用。对POST请求没有问题,但是对get会出现乱码。
        response.setCharacterEncoding(encoding);
        HttpServletRequest req =(HttpServletRequest)request;
        Systems.out.println("过滤");
    }
    
    public void destroy() {
        log.debug("destroy");
    }
}

鉴于对URL不起作用,可以编写请求包装类.

public class RequestEncodingWrapper extends HttpServletRequestWrapper{
    //重新定义getParameter方法
    public String getParameter(String name) {
		String value = getRequest().getParameter(name);
        try{
			if(value != null && ! "".equals(value)) {
                value = new String (value.trim().getBytes("ISO-8859-1"),encoding);
            }
        }catch(UnsupportedEncodingException ex) {
            ex.printStackTrace();
        }
        return value;
    }
}

响应封装器
将响应内容进行统一修改,例如压缩输出内容,替换输出内容等。
编写一个封装器缓存response的内容,这是一个简易的过滤器,

// ResponseReplaceWrapperr.java
public class ResponseReplaceWrapperr extends HttpServletResponseWrapper {

    private  CharArrayWriter charWriter = new CharArrayWriter();

    public ResponseReplaceWrapperr(HttpServletResponse response) {
        super(response);
    }

    public PrintWriter getWriter() throws IOException {
        return new PrintWriter(charWriter);
    }

    public CharArrayWriter getCharWriter() {
        return charWriter;
    }
}

编写内容过滤器:这里使用了Map进行筛选过滤

// ReplaceFilter.java
@WebFilter(
        filterName = "replaceFilter",
        urlPatterns = {"/*"},
        initParams = {
                @WebInitParam(name="filePath",value="replace_ZH.properties")
        }
)

public class ReplaceFilter implements Filter {

    private Properties propert = new Properties();

    public void init(FilterConfig filterConfig) throws ServletException {
        //通过filterConfig
//        String filePath = filterConfig.getInitParameter("filePath");
//        try{
//            propert.load(ReplaceFilter.class.getClassLoader().getResourceAsStream(filePath));
//        }catch (FileNotFoundException ex) {
//            ex.printStackTrace();
//        }catch (IOException ex) {
//            ex.printStackTrace();
//        }
    }

    public void doFilter(ServletRequest request , ServletResponse response , FilterChain chain) throws IOException,ServletException{
        Map<String,String> map = new HashMap<String,String >();
        map.put("赌博","***");
        HttpServletResponse res = (HttpServletResponse) response;
        //实例化响应器包装类
        ResponseReplaceWrapperr resp = new ResponseReplaceWrapperr(res);
        chain.doFilter(request,resp);

        //缓存输出字符
        String outString = resp.getCharWriter().toString();

        //循环替换不合法的字符
        for(Object o:map.keySet()) {
            String key = (String) o;
            outString = outString.replace(key,map.get(key));
        }

        //利用原先的输出字符
        PrintWriter out = res.getWriter();
        out.write(outString);
    }
    public void destroy() {

    }
}

// TestServletFilter.java
@WebServlet(
        urlPatterns = {"/Test.do"},
        loadOnStartup = 0,
        name ="testServletFilter"
)
public class TestServletFilter extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException {
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();
        out.println("<BODY>");
        out.println("dfdasf  赌博");
        out.println("</BODY>");
        out.flush();
        out.close();
    }
}

但运行的时候,赌博会变成***,实现了过滤功能。

以上就是基本内容了!

相关文章:

  • springboot基于协同过滤算法的书籍推荐毕业设计源码101555
  • K-Means聚类算法
  • golang 切片(slice)简单使用
  • SQL Server Reporting Services
  • 加速迈入云原生时代,国产数据库行业要变天
  • PMP每日一练 | 考试不迷路-9.1(包含敏捷+多选)
  • 一体式城市内涝监测站
  • 【高等数学基础进阶】定积分应用
  • RabbitMQ基本使用一
  • CentOS 7.2 正确安装 MySQL 5.6.35
  • 计算机组成与设计-第五章 memory hierarchy(一)
  • 软考高级系统架构设计师系列论文二:论软件的性能优化设计
  • 【CircuitPython】RaspberryPi Pico RP2040 自定义机械键盘实例
  • CentOS7.6安装Rabbitmq
  • 谁说文艺青年开花店必亏,我用3年时间挣了20万
  • 【Leetcode】104. 二叉树的最大深度
  • javascript数组去重/查找/插入/删除
  • Netty 框架总结「ChannelHandler 及 EventLoop」
  • scrapy学习之路4(itemloder的使用)
  • VirtualBox 安装过程中出现 Running VMs found 错误的解决过程
  • 从零开始在ubuntu上搭建node开发环境
  • 猫头鹰的深夜翻译:Java 2D Graphics, 简单的仿射变换
  • 判断客户端类型,Android,iOS,PC
  • 如何借助 NoSQL 提高 JPA 应用性能
  • 如何用vue打造一个移动端音乐播放器
  • 使用 5W1H 写出高可读的 Git Commit Message
  • 学习HTTP相关知识笔记
  • 一个项目push到多个远程Git仓库
  • 赢得Docker挑战最佳实践
  • 用 vue 组件自定义 v-model, 实现一个 Tab 组件。
  • 云大使推广中的常见热门问题
  • 云栖大讲堂Java基础入门(三)- 阿里巴巴Java开发手册介绍
  • 栈实现走出迷宫(C++)
  • ​马来语翻译中文去哪比较好?
  • $(selector).each()和$.each()的区别
  • ${ }的特别功能
  • $NOIp2018$劝退记
  • (JS基础)String 类型
  • (二)linux使用docker容器运行mysql
  • (附源码)spring boot球鞋文化交流论坛 毕业设计 141436
  • (附源码)ssm经济信息门户网站 毕业设计 141634
  • (论文阅读31/100)Stacked hourglass networks for human pose estimation
  • (免费领源码)python+django+mysql线上兼职平台系统83320-计算机毕业设计项目选题推荐
  • (牛客腾讯思维编程题)编码编码分组打印下标题目分析
  • (深入.Net平台的软件系统分层开发).第一章.上机练习.20170424
  • (转)Sql Server 保留几位小数的两种做法
  • .“空心村”成因分析及解决对策122344
  • .java 9 找不到符号_java找不到符号
  • .NET Core MongoDB数据仓储和工作单元模式封装
  • .NET企业级应用架构设计系列之结尾篇
  • /etc/fstab 只读无法修改的解决办法
  • @Autowired多个相同类型bean装配问题
  • @ComponentScan比较
  • @NestedConfigurationProperty 注解用法
  • @select 怎么写存储过程_你知道select语句和update语句分别是怎么执行的吗?