会话技术之 Session

会话技术之 Session

不多废话,先来一个 HelloWorld。

Session 有 get 肯定要先有 set 。

@Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //设置编码
        resp.setContentType("text/html;charset=UTF-8");
        // get 到session
        HttpSession session = req.getSession();
        //有中文,别忘了编码再传进去
        session.setAttribute("HelloWorld", URLEncoder.encode("Hello--World!向世界宣告我的到来!","UTF-8"));
    }

这里分两个Servlet编写

@Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //设置编码
        resp.setContentType("text/html;charset=UTF-8");
        // get 到 session
        HttpSession session = req.getSession();
//        获取到值,别忘了解码
        String  helloWorld =  URLDecoder.decode((String) session.getAttribute("HelloWorld"),"UTF-8");
        //输出一下
        resp.getWriter().write(helloWorld);
    }

Session API

  • long getCreationTime();【获取Session被创建时间】
  • String getId();【获取Session的id】
  • long getLastAccessedTime();【返回Session上一次访问的时间】
  • ServletContext getServletContext();【获取ServletContext对象】
  • void setMaxInactiveInterval(int var1);【设置Session超时时间】
  • int getMaxInactiveInterval();【获取Session超时时间】
  • Object getAttribute(String var1);【获取Session属性】
  • Enumeration getAttributeNames();【获取Session所有的属性名】
  • void setAttribute(String var1, Object var2);【设置Session属性】
  • void removeAttribute(String var1);【移除Session属性,注意==不是销毁该Session
  • void invalidate();【销毁该Session】
  • boolean isNew();【该Session是否为新的】

还有些过时的方法就不一一列举了

Session 有效期

在此之前先说一下 Session 的对象,是一个域对象, 只要Session 还没有被销毁(或者浏览器没有关闭),Servlet 就可以通过 Session 对象进行数据传递。

Session 的生命周期可以通过方法去控制的,直接杀死销毁掉,当然关闭浏览器也是可以直接杀死掉的。

//销毁该 session
        session.invalidate();

还有可以通过时间的预先设置,在此时间段内目标Session 没有被访问,就会进行销毁。这是服务器出于防止内存溢出的考虑,将最久未访问的销毁。不知你是否嗅到了 least recently used (LRU)算法的味道。

这种方式又有三种途径可以去实现,同时作用范围也不尽相同。

在 Tomcat 的 web.xml 配置文件中配置(对部署在该服务器上的应用有效

可以看到默认的配置:

image-20200906215627123

以分钟为单位,默认是 30 分钟。

接下来照葫芦画瓢,修改为 20 分钟:

<session-config>
        <session-timeout>20</session-timeout>
    </session-config>

保存即可生效,再次强调作用域,是部署在该 Tomcat 服务器上的所有项目。如果只是为了练手,验证结果后,别忘了修改回来,或者记住你曾经修改过。

在工程的 web.xml 文件中配置(对 web.xml 文件下的 web 应用有效

配置内容是一样滴。

<session-config>
    <session-timeout>20</session-timeout>
  </session-config>

这里提一个醒,web.xml 在 Servlet 2.3 中,配置的元素必须按照顺序配置。而Servlet 2.4 中是不需要的。那么如何知道自己用的是哪个版本呢?

很简单,你先不按顺序配,如下图所示报红了,那就说明你是 2.3 ,得乖乖按顺序配,提示也会贴心提醒你配置顺序。

image-20200906220234436

在单个Servlet 中通过 setMaxInactiveInterval(int var1) 方法设置 (对单个 Session 有效

既然知道配置方法,那就来看看源码:

image-20200906221831812

从源码可知,是以秒为单位配置的。我们还是来个 20 分钟。

//配置有效期
        session.setMaxInactiveInterval(60*20);

既然三个作用范围,那就会有优先级的问题,这里由小见大,优先级为 3 > 2 > 1

Session 原理

//127.0.0.1:8080/ling/session/putSession 首先由访问地址来看,可知使用的是 Http 协议。Http 协议是一种无状态协议。服务器端并不能通过 Http 协议感知到浏览器中的是哪一个用户。

在浏览器中我们可以看到访问时,会为我们存下名为 JSESSIONID 的 Cookie ,它的值就是 Session的 ID。根本上,浏览器就是根据这一 Cookie 的值来判断是否是否为同一用户,该用哪一个 Session 进行通讯。注意他的生命周期,为 Session 。正合 Session 意。

image-20200907152132373

删除此 Cookie 后,访问一个从 Session中取值的 Servlet ,会报空指针错误,如上所述,因为服务器是根据浏览器发送来名为 JSESSIONID 的 Cookie 来判断使用哪一个 Session,没有 Cookie 提供的 ID ,也就无从去找对应的 Session 了,自然会是服务器端报空指针的错。

image-20200907151233026


那么问题来了,我禁用了 Cookie 的话,那 Session 不就不能用了吗?

是的,正常渠道下是不能用了。

URL 地址重写

注意这两种方法都需要禁用Cookie

关于对目标web 应用禁用 Cookie方法如下:

Java Web规范⽀持通过配置禁⽤Cookie,禁⽤⾃⼰项⽬的Cookie,在META-INF⽂件夹下的context.xml⽂件中修改(没有则创建),并编写如下内容:

<?xml version='1.0' encoding='utf-8'?>
<Context path="/ouzicheng" cookies="false">
</Context>

这里有由HttpServletResponse 提供两种方法进行 URL 地址重写:

  • encodeURL(String url) ;

看源码,我们再决定如何使用它:

image-20200907160427467

这里需要注意源码中也说了,如果浏览器支持 Cookie,或者Session 被关闭(通常是Session是否执行了 invalidate() 方法),这时候是不会进行地址重写的。注意,该方法不能在不支持Cookie的浏览器中生效。

	// 目标url
    String url = "/ling/session/testSession";
    // 重写url,并重定向到目标url
    resp.sendRedirect(resp.encodeURL(url));
  • encodeRedirectURL(String url);

源码里,坑爹的来了:

image-20200907182340737

大概就是,有不同,你们用的时候小心,但是什么不同我就不具体说了。。。这就一个接口,我哪找源码去…..

		// 目标url
        String url = "/ling/session/testSession";
        // 重写url,并重定向到目标url
        resp.sendRedirect(resp.encodeRedirectURL(url));

以上写法下,至少同一站点下,两者的效果并无差别:

image-20200907153817655

要问有什么不同,源码上也看不出来什么。关于不同点,只在网络找到以下说法:

——–来自CSDN 。原文链接 ://blog.csdn.net/SpbDev/article/details/37879549

encodeURL在附加jsessionid之前还对url做了判断处理:如果url为空字符串(长度为0的字符串),则将url转换为完整的URL(http或https开头的);如果url是完整的URL,但不含任何路径(即只包含协议、主机名、端口,例如//127.0.0.1),则在末尾加上根路径符号/。
也就是encodeURL如果进行了编码,则返回的URL一定是完整URL而不是相对路径;而encodeRedirectURL则不对URL本身进行处理,只专注于添加jsessionid参数(如果需要)。

——–来自 STACK OVERFLOW 。问答链接://stackoverflow.com/questions/4944778/whats-the-difference-between-encodeurl-and-encoderedirecturl

The main difference between two is, the implementation of encodeRedirectURL method includes the logic to determine whether the session ID needs to be encoded in the URL in the case when you are redirecting the URL to different context where the session information is not required or invalid. The encodeURL method do not appent the seesion id if the cookies are enabled. In addition to this encodeRedirectURL do not append the session information if the URL is redirected to the different context (web application). Because the rules for making this determination can differ from those used to decide whether to encode a normal link, this method is separete from the encodeURL method.

主要是这句 “如果 URL 重定向到不同的上下文(Web 应用程序),encodeRedirectURL 不追加会话信息。”,然而无从验证,笔者通过两种方法,得到的是同一个 Session 的 ID 值。

.

Tags: