会话
会话:用户打开一个浏览器,然后访问一个web应用中的资源,最后关闭浏览器,这个过程可以称为一次会话。
这种会话就是客户端访问的记录,我们就希望能够将这种记录存储下来,所以就诞生了Cookie和HttpSession这两种技术,用于记录浏览器访问web应用的会话。
保存会话的两种技术
Cookie
它是客户端存储会话的技术。
我们直接先看Cookie类的常用成员方法:
方法 | 功能 |
---|---|
public Cookie(String name, String value) | 该类的构造方法,想要新建一个Cookie类对象就,就必须声明其属性名name和属性值value |
public void setDomain(String domain) | 指定该Cookie对象指定可见的域 |
public String getDomain() | 获取该Cookie对象的可见域 |
public void setMaxAge(int expiry) | 以秒为单位,设置该Cookie对象的最长生命值,负数表示为临时的Cookie,关闭浏览器就销毁了,0表示删除该cookie,正数表示它的最大生命周期 |
public int getMaxAge() | 获取该Cookie对象的最大生命值,未设置生命周期的Cookie数据默认为临时Cookie(MaxAge默认为-1) |
public String getName() | 获取该Cookie的属性名 |
public void setValue(String newValue) | 更改该Cookie对象的属性值 |
public String getValue() | 获取该Cookie对象的属性值 |
我们先获取Cookie数据试试,在此之前,我们没有手动添加过任何Cookie:
package cn.ara.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CookieServlet01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取客户端的cookie数据
Cookie[] cookies = req.getCookies();
//遍历打印输出
if (cookies != null) {
for (Cookie cookie : cookies) {
System.out.println(cookie.getName() + ":" + cookie.getValue());
}
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
使用360浏览器访问该CookieServlet01资源,控制台打印输出为空,说明浏览器在首次访问web应用的资源时是没有Cookie数据的。
接下来我们自行设置一些Cookie数据:
package cn.ara.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CookieServlet02 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//自定义Cookie数据
Cookie cookie = new Cookie("username", "Ara_Hu");
//写回客户端
resp.addCookie(cookie);
cookie = new Cookie("sex","男");
resp.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
我们先打开浏览器的调试功能,找到网络选项,访问CookieServlet02,我们可以看到如下的Cookie数据:
我们发现客户端收到的响应Cookie中包含了我们设置的Cookie数据,然后我们再次请求CookieServlet01,再观察客户端Cookie数据的变化:
这个时候我们明显发现,客户端请求该web应用时的资源时就会将之前的收到的Cookie数据携带上,并且控制台也输出了我们设置的Cookie数据。
修改和删除Cookie数据:
package cn.ara.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CookieServlet03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie[] cookies = req.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if ("username".equals(cookie.getName())) {
//删除username这条cookie数据
cookie.setMaxAge(0);
resp.addCookie(cookie);
}
if ("sex".equals(cookie.getName())) {
//修改sex这条cookie数据
cookie.setValue("女");
resp.addCookie(cookie);
}
}
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
然后我们请求CookieServlet03,发现Cookie数据就会有所变化:
我们发现sex的值已经改变,username的值回来了,但是它的有效期为0,再刷新一次,我们就发现没有username这个Cookie数据了。
当我们把浏览器关闭之后,再次打开访问该web应用时,我们发现请求时Cookie数据就没有了,因为我们并未设置它的有效期。
package cn.ara.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CookieServlet04 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取系统当前时间
long now = System.currentTimeMillis();
//将当前时间存到Cookie中
Cookie cookie = new Cookie("lastRequestTime", now + "");
//设置其有效期为3分钟
cookie.setMaxAge(60*3);
//写回Cookie数据
resp.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
当我们设置了Cookie的有效时间之后,它在不被主动清除的前提下,它的的生命周期就是我们设置的3分钟,在这时间内,我们即使关闭浏览器,它也会存在,直至过期销毁,我们快速关闭浏览器,再访问CookieServlet01,发现Cookie数据还在。
当过去三分钟之后,我们再次访问,就没有Cookie数据了。
注:当Cookie传递中文时,可能会出现问题,我们可以使用以下方式对中文数据进行编码和解码:
//编码
String encode = URLEncoder.encode("编码内容", "编码方式");
//解码
String dncode = URLDecoder.decode("解码内容","解码方式");
这种存储方式虽然减少了服务器端的存储压力,但是直接存储到客户端,数据就会直接暴露给客户端,如果是比较重要的数据,就会出现对应的问题,所以就出来了一种存在服务器端的Session。
HttpSession
它是服务器存储会话的技术,它是依赖于cookie的,它也存储了cookie数据在客户端浏览器,但是它并没有直接将信息显式的放在客户端,而是只放了一个id记录的cookie数据在客户端浏览器,客户端可以根据这个id的cookie数据在web应用中的HttpSession中找到在服务器端存储的数据,这仅限于一次会话,关闭浏览器之后,session就会销毁。
还是先看看其中的方法:
方法 | 作用 |
---|---|
public long getCreationTime(); | 返回该session创建的时间毫秒值 |
public String getId(); | 返回一个字符串,其中包含分配给此会话的唯一标识符id |
public long getLastAccessedTime(); | 返回客户端上次发送与该会话相关的请求的时间 |
public ServletContext getServletContext(); | 获取该web应用的ServletContext对象 |
public void setMaxInactiveInterval(int interval); | 设置该session的最大有效时间 |
public int getMaxInactiveInterval(); | 获取该session的有效时间 |
public Object getAttribute(String name); | 获取该session域中的指定属性值 |
public Enumeration getAttributeNames(); | 获取该session对象中存储的所有属性名 |
public void setAttribute(String name, Object value); | 在该session中设置指定属性,如果已存在,则替换之前的值 |
public void removeAttribute(String name); | 移除该session中指定的属性 |
public void invalidate(); | 销毁该session,使之无效 |
public boolean isNew(); | 判断该session是不是新的,就是判断客户端在请求服务器端的时候是否携带了标记sessionid的的cookie的值,存在返回true,不存在返回false |
package cn.ara.servlet;
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.util.Date;
import java.util.Enumeration;
public class SessionServlet01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取session对象,
// 如果请求中带有JSESSIONID的Cookie,就获取该id指定的session,
// 否则创建一个新的JSESSIONID,在响应客户端时以cookie的形式写回
HttpSession session = req.getSession();
//打印session的id和它是否是新创建的 F4E82D22E3B3787F58FD8AF2FFA9EA0D:true
System.out.println(session.getId() + ":" + session.isNew());
//打印创建时间 第一次访问该Servlet的时间
System.out.println("CreationTime:" + new Date(session.getCreationTime()));
//打印上次访问时间
System.out.println("LastAccessedTime:" + new Date(session.getLastAccessedTime()));
//循环遍历session中的内容
Enumeration<String> attributeNames = session.getAttributeNames();
while (attributeNames.hasMoreElements()) {
String name = attributeNames.nextElement();
Object attribute = session.getAttribute(name);
System.out.println(name + "---" + attribute);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
用浏览器访问SessionServlet01,并抓包,观察它的Cookie变化如下:
我们发现,它响应了一个JSESSIONID的cookie数据回来,这个Cookie数据并不是我们手动设置的,对比上面的实验和控制台的打印输出,可以推断出这个cookie数据就是记录的Session的id值,这也说明了session的实现也有cookie的“功劳”。
我们存储一些数据在session中:
package cn.ara.servlet;
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.util.Date;
import java.util.Enumeration;
public class SessionServlet02 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取session对象
HttpSession session = req.getSession();
//打印session的id和它是否是新创建的
System.out.println(session.getId() + ":" + session.isNew());
session.setAttribute("name","Ara_hu");
session.setAttribute("sex","男");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
然后我们重启服务器,重新访问SessionServlet01,让其重新生成新的Session(因为web应用关闭之后,存放在服务器中的session就会对应销毁,但是客户端的cookie数据还存在,所以会出现请求时包含cookie数据,但是服务器端已经找不到这个指定的session了,所以它会创建一个session,返回的cookie就是一个全新的sessionId):
然后再访问SessionServlet02,让它存一些数据在session中,
控制台输出:4EE5EFB3CEED28FF3218D17931F54CBA:false
我们再访问SessionServlet01,查看控制台是否打印出我们设置在session的属性
我们发现后两次请求时,服务器端就没有返回新的JSESSIONID数据了,然后我们将浏览器关闭,重新访问SessionServlet01,再观察控制台输出:
这时之前记录sessionId的Cookie数据就自行销毁了,这也说明session的有效期仅限于一次会话,会话结束后,session的值就会自行销毁。
我们也能手动的设置session的有效期,在web.xml中添加如下配置:
<session-config>
<!--设置session10分钟失效-->
<session-timeout>10</session-timeout>
</session-config>
这个配置和public void setMaxInactiveInterval(int interval);方法的效果一致,如果用户一直保持着会话但是长时间未响应,这个时间之后,session就会自动失效。
Cookie和Session的区别
Cookie | Session |
---|---|
存储在客户端 | 存储在服务器(服务器会将sessionId以cookie的形式写回客户端) |
Cookie不安全 | Session相对比较安全 |
Cookie数据有限制,只能存储字符串类型的数据,而且单个Cookie数据不能超过4K,浏览器一般对一个站点限制20个Cookie,一个浏览器最多能存储300个Cookie | Session没有对存储量进行限制,它还能保存复杂的数据类型 |
Cookie的有效期相对更长 | Session则很容易失效(浏览器关闭就失效了) |