前言
前面了解了HTTP的无状态性
为了解决无状态的问题,有两种技术:Cookie和Session
学习了Cookie,现在来了解Session
目录
- Session简介
- Session API
- Session的使用
3.1. 存取数据
3.2. 生命周期
3.3. 钝化活化 - Session与Cookie
4.1. URL地址重写
4.2. Session与Cookie的区别
4.3. 联合使用 - 总结
Session简介
无状态是指:服务器不会保存客户端-服务器会话的临时数据,每一次会话都是独立的
这会造成每次购物操作都需要重新登录,解决办法有两个:Cookie与Session
Cookie存储在客户端,Session存储在服务器
Cookie技术:
可以看到,当服务器给客户端创建了Cookie后,如果想要保持上下文的相关信息,就都需要在Request中带上Cookie
这就造成了一个很大的局限:Cookie如果很多,就会增加客户端与服务器的传输数据,而且由于浏览器会对Cookie做一定的限制,就不能保持太多的信息
为了解决这个局限,建立在Cookie上的Session就出现了
Session的作用就是在服务端保持数据,然后传递给客户端一个叫JSESSIONID 的Cookie,客户端仅需带着这个JSESSIONID 就可以获得保持的数据
Session API
在JavaWeb中,使用Session调用的是HttpSession接口的方法
既然是接口及抽象方法,那么具体是谁实现的呢?
HttpSession的具体实现是由服务器来完成的
比如tomcat,在tomcat的src中java\org\apache\catalina\session下的类StandardSession实现了HttpSession
(这个关系到Tomcat源码,就不往下深究了)
这些API各有什么作用?
API | 作用 |
---|---|
Object getAttribute(String); | 获取Session属性 |
Enumeration getAttributeNames(); | 获取Session所有的属性名 |
long getCreationTime() | 获取Session被创建时间 |
String getId() | 获取Session的id |
long getLastAccessedTime() | 返回Session最后活跃的时间 |
ServletContext getServletContext(); | 获取ServletContext对象 |
void setMaxInactiveInterval(int var1); | 设置Session超时时间 |
int getMaxInactiveInterval() | 获取Session超时时间 |
void setAttribute(String var1, Object var2); | 设置Session属性 |
void removeAttribute(String var1); | 移除Session属性 |
void invalidate(); | 销毁该Session |
boolean isNew(); | 判断该Session是否为新的 |
Session的使用
Session是一个域对象,类似与Servletcontext,可以用来保存数据、保存会话临时数据
存取数据
编写一个存入数据的Servlet:
package Session;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class SessionTest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//得到Session
HttpSession httpSession = req.getSession();
//存入数据
httpSession.setAttribute("name","zhangsan");
}
}
编写一个得到数据的Servlet:
package Session;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
public class SessionTest1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
String name = (String) session.getAttribute("name");
PrintWriter writer = resp.getWriter();
writer.write(name);
}
}
在web.xml中关联这两个Servlet
访问:http://localhost:8080/sessionTest
服务器给了客户端一个JSESSIONID
访问:http://localhost:8080/sessionTest1
客户端带着这个JSESSIONID可以得到name值
生命周期
- Session在用户第一次访问服务器jsp动态资源会被自动创建(Jsp本质上是一个Servlet,源码中调用了request.getSession()方法)或者servlet中调用request.getSession()时创建,Session对象保存在内存里
- 既然动态资源会创建,静态资源会吗?当然是不会,打开一个HTML并不会调用到Servlet
还记得前面这个图,Web服务器直接调用静态页面,并不会经过Servlet容器
-
活跃时间,前面有一个API是getLastAccessedTime,获得Session最后一次活跃时间,也就是说服务器会更新客户端访问Session的时间
-
超时时间,随着客户的创建,Session会越来越多,通过超时时间来约束Session,Session的默认超时时间是30分钟
这是Tomcat的设计,可以在web.xml中找到
修改方式:
(1)在Tomcat下的web.xml中修改,会使所有使用该Tomcat的Web工程都改变(这个web.xml类似于所有工程配置的父类)
(2)在自己的工程的web.xml中设置,仅会影响自己的工程
(3)通过API设置:setMaxInactiveInterval(int var1),这里的单位是秒
- 可以使 Session 对象释放的情况:
Session对象空闲时间超时,自动释放
手动释放Session (invalidate方法)
对象被强制失效
Web 应用卸载
服务器进程停止
注意:Session和Cookie所说的超时是不一样的,Session的超时时间是指空闲时间,多久没有访问该Session;而Cookie是超时时间是当Cookie创建时开始计算,不管有没有访问Cookie
钝化活化
钝化:当服务器正常关闭,还存活着的Session,会序列化到磁盘上,而不再占用内存空间,以文件(“SESSIONS.ser”)的形式存储在tomcat的work目录下(IDEA打开的存放在IDEA设置的本地地址)
虽然记事本打开乱码了,但是大致看的出确实是我们的Session
活化:Session 被钝化后,服务器再次调用Session对象时,将 Session对象由磁盘中加载到内存中使用
注意:
- 如果Session中数据想要随着Session一起钝化,需要实现序列化接口Serializable
- 钝化后,Session不会超过Session超时时间而过期,这个文件会保存到下次启动服务器时消失
启动后:
- 多个Session钝化会保存在一个文件中
如果想要设置钝化
(1)在tomcat里面 conf/context.xml 里面配置,对所有的运行在这个服务器的项目生效
(2)在conf/Catalina/localhost/context.xml 配置 ,对 localhost生效
(3)在自己的web工程项目中的 META-INF/context.xml,只对当前的工程生效
一个Context就是一个工程
配置:
<Context>
<Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1">
<Store className="org.apache.catalina.session.FileStore" directory="D:"/>
</Manager>
</Context>
maxIdleSwap代表空闲时间,一分钟没使用Session就钝化
directory表示钝化文件存储位置
Session与Cookie
使用Session时,服务器会自动创建一个Cookie,并设置Cookie属性为:
JSESSIONID=E7B846EA2368DAD4BDF71E1274AA42F2
可以看出Session是基于Cookie实现的,Session的真实数据存储在服务器,客户端凭借JSESSIONID可以使用该Session数据
该Cookie是服务器自己创建的,所有的属性都是默认值,maxAge值默认是-1,该Cookie仅当前浏览器使用,不可被持久化
如果浏览器禁用了Cookie,怎么使用Session?
URL地址重写
在浏览器中禁用Cookie
禁用了之后,就没有JSESSIONID的cookie了
访问sessionTest1当然会报错
HttpServletResponse类提供了两个URL地址重写的方法:
-
encodeURL(String url)
-
encodeRedirectURL(String url)
随便使用一个:在SessionTest中加上URL重写
String url = "/sessionTest1";
resp.sendRedirect(resp.encodeURL(url));
输入地址:http://localhost:8080/sessionTest
自动跳到sessionTest1,且在URL中带上参数JSESSIONID
浏览器确实没有Cookie
URL重写方法会自动判断该浏览器是否支持Cookie,如果不支持Cookie,重写后的URL地址会带上jsessionid
Session与Cookie的区别
- 存储位置、隐私安全
Session是存储在服务器上,对客户是透明的,安全性较高(也高不到哪去,毕竟也是通过Cookie实现)
Cookie是存储在客户端上,客户可见,即安全性低 - 存储方式
Session可以存储任何数据键值对(类似与Map)
Cookie只能存储字符串,而且因为版本属性原因,对存储的字符串有诸多要求 - 有效期
Session的超时设置是空闲时间,而且浏览器关闭Session就会失效
Cookie的超时设置是从创建开始计算,而且可以持久化 - 资源消耗
Session是保存在服务器的,每个用户都会产生一个Session,如果是并发访问的用户非常多,是不能使用Session的,Session会消耗大量的内存。
Cookie是保存在客户端的。不占用服务器的资源。像baidu、Sina这样的大型网站,一般都是使用Cookie来进行会话跟踪 - 浏览器支持
浏览器禁用Cookie,Session可以通过URL重写实现
而Cookie无效 - 跨域名
Session是基于一个默认属性的Cookie,即无法跨域名,仅在当前域名使用
Cookie可以设置domain属性跨域名 - 限制访问
Session基于一个默认属性的Cookie,path设置为/,所有当前浏览器都可以访问
Cookie可以设置path属性,限制Servlet访问
联合使用
既然Session受限与Cookie的默认属性,那么我们自定义一个Cookie,在将Session存储进Cookie中不就可以了?
Cookie cookie = new Cookie("JSESSIONID",session.getId());
当然,也可以暴力点,直接把Tomcat中的Session设置改了
修改Tomcat的配置文件server.xml
一个contenxt就是一个会话
在< Host>节点下增加< Context>节点,并设置path,docBase,debug和reloadable属性和一些session配置
<Context path="" docBase="webapp" debug="0" reloadable="false" sessionCookiePath="/" sessionCookieName=""/>
Tomcat配置
一般我们将WEB应用打包成WAR格式,然后拷贝到TOMCAT的WEBAPP目录下进行发布
在正常情况下,TOMCAT将自动解压WAR文件并生成对应的目录,而且会在内存中动态创建该WEB应用对应的CONTEXT路径。比如我们的WAR文件是JavaWeb.war,那么解压后的目录就是JavaWeb目录,在内存中动态创建的Context路径就是/JavaWeb。
在开发阶段这样的默认行为是可以接受的,但是在正式部署的情况下,我们通常需要修改应用的Context路径以适应我们的需要
如果我们想让用户仅仅输入域名来访问我们的JavaWeb应用,我们就需要修改Tomcat的Context设置
可以在conf/server.xml下增加一个context结点
<Context path="/javaweb" docBase="D:/JavaProject/JavaWeb" debug="0" reloadable="true">
<WatchedResource>WEB-INF/web.xml</WatchedResource>
</Context>
总结
- HttpSession是抽象类,通过服务器实现Session(这就是为什么可以通过Request得到Session)
- Session有很多API,了解了一下
- Session是一个域对象,可以存取任何类型的数据
- Session是基于一个默认的Cookie实现,Cookie属性为:JSESSIONID=value,浏览器仅需带着这个Cookie就可得到服务器端的Session数据
- 因为Session是基于一个默认的Cookie,受限于默认Cookie的属性
- 可以联合Session和Cookie,实现需求