整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

干货,一文带你超详细了解Session的原理及应用

干货,一文带你超详细了解Session的原理及应用

ession 简介

session 是我们 jsp 九大隐含对象的一个对象。

session 称作域对象,他的作用是保存一些信息,而 session 这个域对象是一次会话期间使用同一个对象。所以这个对象可以用来保存共享数据。

  • 使用 Cookie 有一个非常大的局限,就是如果 Cookie 很多,则无形的增加了客户端与服务端的数据传输量。而且由于浏览器对 Cookie 数量的限制,注定我们不能再 Cookie 中保存过多的信息,于是 Session 出现。
  • Session 的作用就是在服务器端保存一些用户的数据,然后传递给用户一个名字为JSESSIONID 的 Cookie,这个 JESSIONID 对应这个服务器中的一个 Session 对象,通过它就可以获取到保存用户信息的 Session。

session 是基于 cookie 的。

在用户第一次使用 session 的时候(访问 jsp 页面会获取 session,所以一般访问 index.jsp 就算是第一次使用 session 了),服务器会为用户创建一个 session 域对象。使用 jsessionid 和这个对象关联,这个对象在整个用户会话期间使用。响应体增加 set-cookie:jsessionid=xxx 的项。用户下次以后的请求都会携带 jsessionid 这个参数,我们使用 request.getSession()的时候,就会使用 jsessionid 取出 session 对象。

session 原理图:


HttpSession 的生命周期

什么时候创建 HttpSession 对象

①. 对于 JSP: 是否浏览器访问服务端的任何一个 JSP, 服务器都会立即创建一个 HttpSession 对象呢?

不一定。

  • 若当前的 JSP 是客户端访问的当前 WEB 应用的第一个资源,且 JSP 的 page 指定的 session 属性值为 false,则服务器就不会为 JSP 创建一个 HttpSession 对象;
  • 若当前 JSP 不是客户端访问的当前 WEB 应用的第一个资源,且其他页面已经创建一个 HttpSession 对象,则服务器也不会为当前 JSP 页面创建一个 HttpSession 对象,而会把和当前会话关联的那个 HttpSession 对象返回给当前的 JSP 页面.

②. 对于 Serlvet: 若 Serlvet 是客户端访问的第一个 WEB 应用的资源,则只有调用了 request.getSession() 或 request.getSession(true) 才会创建 HttpSession 对象

page 指令的 session=“false“ 表示什么意思?

当前 JSP 页面禁用 session 隐含变量!但可以使用其他的显式的 HttpSession 对象

在 Serlvet 中如何获取 HttpSession 对象?

request.getSession(boolean create): 

create 为 false, 若没有和当前 JSP 页面关联的 HttpSession 对象, 则返回 null; 若有, 则返回 true

create 为 true, 一定返回一个 HttpSession 对象. 若没有和当前 JSP 页面关联的 HttpSession 对象, 则服务器创建一个新的HttpSession 对象返回, 若有, 直接返回关联的.

request.getSession(): 等同于 request.getSession(true)

什么时候销毁 HttpSession 对象

①. 直接调用 HttpSession 的 invalidate() 方法: 该方法使 HttpSession 失效

②. 服务器卸载了当前 WEB 应用.

③. 超出 HttpSession 的过期时间.

④. 并不是关闭了浏览器就销毁了 HttpSession.

session 使用

获取 session 对象

HttpSession session=request.getSession();

session 是我们的四大域对象之一。用来保存数据。常用的方法

session.setAttribute("user", new Object()); session.getAttribute("user");
session.setMaxInactiveInterval(60*60*24);//秒为单位
session.invalidate();//使 session 不可用

Session 时 效

①、基本原则

Session 对象在服务器端不能长期保存,它是有时间限制的,超过一定时间没有被访问过的 Session 对象就应该释放掉,以节约内存。所以 Session 的有效时间并不是从创建对象开始计时,到指定时间后释放——而是从最后一次被访问开始计时,统计其“空闲” 的时间。

②、默认设置

在全局 web.xml 中能够找到如下配置:

<session-config>
 <session-timeout>30</session-timeout>
</session-config>

③、手工设置

session.setMaxInactiveInterval(int seconds) 
session.getMaxInactiveInterval()

④、强制失效

session.invalidate()

⑤、可以使 Session 对象释放的情况

Session 对象空闲时间达到了目标设置的最大值,自动释放

Session 对象被强制失效

Web 应用卸载服务器进程停止

URL 重写

在整个会话控制技术体系中,保持 JSESSIONID 的值主要通过 Cookie 实现。但 Cookie 在浏览器端可能会被禁用,所以我们还需要一些备用的技术手段,例如:URL 重写。

1)URL 重写其实就是将 JSESSIONID 的值以固定格式附着在 URL 地址后面,以实现保持

JSESSIONID,进而保持会话状态。这个固定格式是:URL;jsessionid=xxxxxxxxx

例如:

targetServlet;jsessionid=F9C893D3E77E3E8329FF6BD9B7A09957

2) 实 现 方 式 :

response.encodeURL(String)
response.encodeRedirectURL(String)

例如:

//1.获取Session对象
HttpSession session=request.getSession();
//2.创建目标URL地址字符串
String url="targetServlet";
//3.在目标URL地址字符串后面附加JSESSIONID的值
url=response.encodeURL(url);
//4.重定向到目标资源
response.sendRedirect(url);

Session 的活化和钝化

Session 机制很好的解决了 Cookie 的不足,但是当访问应用的用户很多时,服务器上就会创建非常多的 Session 对象,如果不对这些 Session 对象进行处理,那么在 Session 失效之前,这些 Session 一直都会在服务器的内存中存在。那么就,就出现了 Session 活化和钝化的机制。

1)Session 钝化:

Session 在一段时间内没有被使用时,会将当前存在的 Session 对象序列化到磁盘上,而不 再 占 用 内 存 空 间 。

2)Session 活化:

Session 被钝化后,服务器再次调用 Session 对象时,将 Session 对象由磁盘中加载到内存中使用。

如果希望 Session 域中的对象也能够随 Session 钝化过程一起序列化到磁盘上,则对象的实现类也必须实现java.io.Serializable 接口。不仅如此,如果对象中还包含其他对象的引用,则被关联的对象也必须支持序列化,否则会抛出异常:java.io.NotSerializableException

表单重复提交问题

什么是表单重复提交?

同一个表单中的数据内容多次提交到服务器。 危害:

服务器重复处理信息,负担加重。

如果是保存数据可能导致保存多份相同数据。

几种重复提交

1)提交完表单后,直接刷新页面,会再次提交。

- 根本原因:Servlet 处理完请求以后,直接转发到目标页面。

- 这样整一个业务,只发送了一次请求,那么当你在浏览器中点击刷新按钮或者狂按 f5,会一直都会刷新之前的请求

解决方案:使用重定向跳转到目标页面

2)提交表单后,由于网速差等原因,服务器还未返回结果,连续点击提交按钮,会重 复提交。

- 根本原因:按钮可以多次点击

- 解决方案:通过 js,使得按钮只能提交一次。

$(“#form1”).submit(function(){
 $(“#sub_btn”).prop(“disabled”,true);
})

3)表单提交后,点击浏览器回退按钮,不刷新页面,点击提交按钮再次提交表单

- 根本原因:服务器并不能识别请求是否重复。

- 解决方案:使用 token 机制。

1、页面生成时,产生一个唯一的 token 值。将此值放入 session

2、表单提交时,带上这个 token 值。

3、服务端验证 token 值存在,则提交表单,然后移除此值。验证 token 不存在,说明是之前验证过一次被移除了,所以是重复请求。不予处理

原理:

代码:

jsp 页面

<%
  String token=System.currentTimeMillis() + ""; 
  request.getSession().setAttribute(token, "");
%>
<div>
  <h1>测试表单重复提交</h1>
  <form action="login" method="get">
    用户名:<input name="username" type="text"/>
    密码:<input name="password" type="password">
    <input name="token" value="<%=token%>">
    <input type="submit">
  </form>
  <hr>
</div>

Servlet

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 HttpSession session=request.getSession(); 
 String token=request.getParameter("token"); 
 Object attribute=session.getAttribute(token);
 response.setContentType("text/html;charset=UTF-8");
 if(attribute!=null){ 
 session.removeAttribute(token);
 response.getWriter().write("请求成功!");
 }else{
 response.getWriter().write("请不要重复请求!");
 }
}

其实防止重复提交的核心就是让服务器有一个字段能来识别此次请求是否已经执行。 这个字段需要页面传递过来,因为只要回退回去的页面,字段都是一致的。不会变化, 通过这个特性我们想到了 token 机制来防止重复提交

生苦短,我用 Python


如果我的文章对您有帮助,请关注支持下作者的公众号:极客挖掘机,您的关注,是对小编坚持原创的最大鼓励:)

前文传送门:

小白学 Python 爬虫(1):开篇

小白学 Python 爬虫(2):前置准备(一)基本类库的安装

小白学 Python 爬虫(3):前置准备(二)Linux基础入门

小白学 Python 爬虫(4):前置准备(三)Docker基础入门

小白学 Python 爬虫(5):前置准备(四)数据库基础

小白学 Python 爬虫(6):前置准备(五)爬虫框架的安装

小白学 Python 爬虫(7):HTTP 基础

小白学 Python 爬虫(8):网页基础

小白学 Python 爬虫(9):爬虫基础


引言

先说一个题外话,今天老司机翻车了,内容小编今天来不及写了,后面会整理下,分享给大家。

在介绍 Session 和 Cookies 之前,先介绍一个另外的概念 —— 静态网页和动态网页。

静态网页

静态网页就是我们上一篇写的那种 html 页面,后缀为 .html 的这种文件,直接部署到或者是放到某个 web 容器上,就可以在浏览器通过链接直接访问到了,常用的 web 容器有 Nginx 、 Apache 、 Tomcat 、Weblogic 、 Jboss 、 Resin 等等,很多很多。

如果说要举例子的话那么小编的个人博客站:https://www.geekdigging.com/ 就是一个纯粹的静态网页。

这种网页的内容是通过纯粹的 HTML 代码来书写,包括一些资源文件:图片、视频等内容的引入都是使用 HTML 标签来完成的。

它的好处当然是加载速度快,编写简单,访问的时候对 web 容器基本上不会产生什么压力。但是缺点也很明显,可维护性比较差,不能根据参数动态的显示内容等等。

有需求就会有发展么,这时动态网页就应运而生了。

动态网页

我们先不说动态网页的概念,先说说有哪些网站是由动态网页来构建的。大家常用的某宝、某东、拼夕夕等网站都是由动态网页组成的。

动态网页可以解析 URL 中的参数,或者是关联数据库中的数据,显示不同的网页内容。

现在各位同学访问的网站大多数都是动态网站,它们不再简简单单是由 HTML 堆砌而成,可能是由 JSP 、 PHP 等语言编写的,当然,现在很多由前端框架编写而成的网页小编这里也归属为动态网页。

说到动态网页,各位同学可能使用频率最高的一个功能是登录,像各种电商类网站,肯定是登录了以后才能下单买东西。那么,问题来了,后面的服务端是如何知道当前这个人已经登录了呢?

HTTP/1.1

现在大多数的网站使用的协议都是 HTTP/1.1 ,而 HTTP/1.1 最大的特点就是无状态、无连接的。

无状态就是指 HTTP 协议对于请求的发送处理是没有记忆功能的,也就是说每次 HTTP 请求到达服务端,服务端都不知道当前的客户端(浏览器)到底是一个什么状态。客户端向服务端发送请求后,服务端处理这个请求,然后将内容响应回客户端,完成一次交互,这个过程是完全相互独立的,服务端不会记录前后的状态变化,也就是缺少状态记录。

这就产生了上面的问题,服务端如何知道当前在浏览器面前操作的这个人是谁?

其实,在用户做登录操作的时候,服务端会下发一个类似于 token 凭证的东西返回至客户端(浏览器),有了这个凭证,才能保持登录状态。

那么这个凭证是什么?

这就是本篇文章要解释的核心内容,Session 和 Cookies 了。

Session 是会话的意思,会话是产生在服务端的,用来保存当前用户的会话信息,而 Cookies 是保存在客户端(浏览器),有了 Cookie 以后,客户端(浏览器)再次访问服务端的时候,会将这个 Cookie 带上,这时,服务端可以通过 Cookie 来识别本次请求到底是谁在访问。

可以简单理解为 Cookies 中保存了登录凭证,我们只要持有这个凭证,就可以在服务端保持一个登录状态。

在爬虫中,有时候遇到需要登录才能访问的网页,只需要在登录后获取了 Cookies ,在下次访问的时候将登录后获取到的 Cookies 放在请求头中,这时,服务端就会认为我们的爬虫是一个正常登录用户。

Session 保持

那么,Cookies 是如何保持会话状态的呢?

在客户端(浏览器)第一次请求服务端的时候,服务端会返回一个请求头中带有 Set-Cookie 字段的响应给客户端(浏览器),用来标记是哪一个用户,客户端(浏览器)会把这个 Cookies 给保存起来。

我们来使用工具 PostMan 来访问下某东的登录页,看下返回的响应头:

当我们输入好用户名和密码时,客户端会将这个 Cookies 放在请求头一起发送给服务端,这时,服务端就知道是谁在进行登录操作,并且可以判断这个人输入的用户名和密码对不对,如果输入正确,则在服务端的 Session 记录一下这个人已经登录成功了,下次再请求的时候这个人就是登录状态了。

如果客户端传给服务端的 Cookies 是无效的,或者这个 Cookies 根本不是由这个服务端下发的,或者这个 Cookies 已经过期了,那么接下里的请求将不再能访问需要登录后才能访问的页面。

所以, Session 和 Cookies 之间是需要相互配合的,一个在服务端,一个在客户端。

Cookies

我们还是打开某东的网站,看下这些 Cookies到底有哪些内容:

具体操作方式还是在 Chrome 中按 F12 打开开发者工具,选择 Application 标签,点开 Cookies 这一栏。

  • Name:这个是 Cookie 的名字。一旦创建,该名称便不可更改。
  • Value:这个是 Cookie 的值。
  • Domain:这个是可以访问该 Cookie 的域名。例如,如果设置为 .jd.com ,则所有以 jd.com ,结尾的域名都可以访问该Cookie。
  • Max Age:Cookie 失效的时间,单位为秒,也常和 Expires 一起使用。 Max Age 如果为正数,则在 Max Age 秒之后失效,如果为负数,则关闭浏览器时 Cookie 即失效,浏览器也不会保存该 Cookie 。
  • Path:Cookie 的使用路径。如果设置为 /path/ ,则只有路径为 /path/ 的页面可以访问该 Cookie 。如果设置为 / ,则本域名下的所有页面都可以访问该 Cookie 。
  • Size:Cookie 的大小。
  • HTTPOnly:如果此项打勾,那么通过 JS 脚本将无法读取到 Cookie 信息,这样能有效的防止 XSS 攻击,窃取 Cookie 内容,可以增加 Cookie 的安全性。
  • Secure:如果此项打勾,那么这个 Cookie 只能用 HTTPS 协议发送给服务器,用 HTTP 协议是不发送的。

那么有的网站为什么这次关闭了,下次打开的时候还是登录状态呢?

这就要说到 Cookie 的持久化了,其实也不能说是持久化,就是 Cookie 失效的时间设置的长一点,比如直接设置到 2099 年失效,这样,在浏览器关闭后,这个 Cookie 是会保存在我们的硬盘中的,下次打开浏览器,会再从我们的硬盘中将这个 Cookie 读取出来,用来维持用户的会话状态。

第二个问题产生了,服务端的会话也会无限的维持下去么,当然不会,这就要在 Cookie 和 Session 上做文章了, Cookie 中可以使用加密的方式将用户名记录下来,在下次将 Cookies 读取出来由请求发送到服务端后,服务端悄悄的自己创建一个用户已经登录的会话,这样我们在客户端看起来就好像这个登录会话是一直保持的。

理解误区

当我们关闭浏览器的时候会自动销毁服务端的会话,这个是错误的,因为在关闭浏览器的时候,浏览器并不会额外的通知服务端说,我要关闭了,你把和我的会话销毁掉吧。

因为服务端的会话是保存在内存中的,虽然一个会话不会很大,但是架不住会话多啊,硬件毕竟是会有限制的,不能无限扩充下去的,所以在服务端设置会话的过期时间就非常有必要。

当然,有没有方式能让浏览器在关闭的时候同步的关闭服务端的会话,当然是可以的,我们可以通过脚本语言 JS 来监听浏览器关闭的动作,当浏览器触发关闭动作的时候,由 JS 像服务端发起一个请求来通知服务端销毁会话。

由于不同的浏览器对 JS 事件的实现机制不一致,不一定保证 JS 能监听到浏览器关闭的动作,所以现在常用的方式还是在服务端自己设置会话的过期时间。

参考

https://baike.baidu.com/item/cookie/1119

建工程

File>New>Web Project


填写项目名称Project Name,举例为andylife,这里使用的是默认存储路径,如需修改,把下面Use default location前面的勾去掉,并在Directory填写自己的地址


设置完成后点击Finish,新建完成

编写内容

打开WebRoot目录下的index.jsp,把编码改为utf-8


网页修改内容,显示日期时间源码:

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>

<%

String path=request.getContextPath();

String basePath=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";

%>

<html>

<body>

<%

//设置刷新页面的时间,每隔1秒钟刷新一次

response.setHeader("refresh", "10");

%>

当前的系统时间是:

<%

//输出当前时间

Calendar now=Calendar.getInstance();

out.print(now.get(Calendar.YEAR) + "年");

out.print(now.get(Calendar.MONTH) + 1 + "月");

out.print(now.get(Calendar.DATE) + "日");

out.print(now.get(Calendar.HOUR_OF_DAY) + "时");

out.print(now.get(Calendar.MINUTE) + "分");

out.print(now.get(Calendar.SECOND) + "秒 ");

out.print("星期");

switch (now.get(Calendar.DAY_OF_WEEK)) {

case 1:

out.print("日");

break;

case 2:

out.print("一");

break;

case 3:

out.print("二");

break;

case 4:

out.print("三");

break;

case 5:

out.print("四");

break;

case 6:

out.print("五");

break;

case 7:

out.print("六");

}

out.print(""+"<hr>");

out.print("ID Things need to be done IF DAILY "+"<br>");

%>

</body>

<body>

<%

out.print(""+"<hr>");

%>

</body>

</html>

运行

把页面部署到Tomcat上

点击ADD,然后选择我们的Tomcat服务器,点击Finish,然后点OK





点击旁边图标的小三角形,选择服务器,并运行

当我们在控制台看到此信息时,服务器就开启成功了


前端浏览

在浏览器输入网址:http://localhost:8080/life/index.jsp

它表示的是,在本地8080端口下,life中的index.jsp