什么是会话
从浏览器访问服务器开始,到访问服务器结束,浏览器关闭为止的这段时间内容产生的多次请求和响应,合起来叫做浏览器和服务器之间的一次会话
会话管理作用
共享数据用的,并且是在不同请求间实现数据共享。
会话技术是为了解决客户端浏览器和服务端的通信问题。用户在网页上的操作会产生很多重要的并且需要被保存的数据。比如购物车信息,可能我们此时不会清空购物车,那么购物车的信息需要被完整的保存下来。再比如登陆网站只需要登陆一次,再登陆网站的其他网页时就不需要重复登陆了,这是因为用户信息被保存了。
数据能否用HttpServletRequest
或者ServletContext
保存呢?
不能用 HttpServletRequest 的原因:我们的一次会话中,存在多次请求和响应,而浏览器客户端的每一次请求都会产生一个 HttpServletRequest 对象,它只会保存此次请求的信息,例如放入购物车与购买付款是不同的请求,很显然数据没有得到很好的保存处理
不能用 ServletContext 的原因:ServletContext对象是被整个web应用所共享的,将数据都存到这里,无疑会无法区分具体信息的归属
会话管理分类
客户端会话管理技术
它是把要共享的数据保存到了客户端(也就是浏览器端)。每次请求时,把会话信息带到服务器,从而实现多次请求的数据共享。
服务端会话管理技术
它本质仍是采用客户端会话管理技术,只不过保存到客户端的是一个特殊的标识,并且把要共享的数据保存到了服务端的内存对象中。每次请求时,把这个标识带到服务器端,然后使用这个标识,找到对应的内存空间,从而实现数据共享。
CookieCookie 概述
Cookies是服务器在本地机器上存储的小段文本并随每一个请求发送至同一服务器,是在客户端保持状态的方案,是客户端浏览器的缓存文件,里面记录了客户浏览器访问网站的一些内容。同时,也是HTTP协议请求和响应消息头的一部分
常用API
属性名称 | 属性作用 | 是否重要 |
name | cookie的名称 | 必要属性 |
value | cookie的值(不能是中文) | 必要属性 |
path | cookie的路径 | 重要 |
domain | cookie的域名 | 重要 |
maxAge | cookie的生存时间。 | 重要 |
version | cookie的版本号。 | 不重要 |
comment | cookie的说明。 | 不重要 |
注意细节
Cookie有大小,个数限制。每个网站最多只能存20个cookie,且大小不能超过4kb。同时,所有网站的cookie总数不超过300个。
当删除Cookie时,设置maxAge值为0。当不设置maxAge时,使用的是浏览器的内存,当关闭浏览器之后,cookie将丢失。设置了此值,就会保存成缓存文件(值必须是大于0的,以秒为单位)。
常用方法
返回值 | 方法 | 作用 |
String | getName() | 返回cookie的name |
String | getValue() | 返回当前cookie的value |
void | setMaxAge() | 设置Cookie最大存活时间 |
构造方法:
Cookie(String name, String value)
- 通过指定的名称和值构造一个Cookie
- Cookie的名称必须遵循RFC 2109规范。这就意味着,它只能包含ASCII字母数字字符,
- 不能包含逗号、分号或空格或以$字符开头。
- 创建后无法更改cookie的名称。
- 该值可以是服务器选择发送的任何内容。
- 它的价值可能只有服务器才感兴趣。
- 创建之后,可以使用setValue方法更改cookie的值。
向浏览器添加Cookie:
public void addCookie(Cookie cookie);
- 添加Cookie到响应中。此方法可以多次调用,用以添加多个Cookie。
从服务器端获取Cookie:
public Cookie[] getCookies();
- 这是HttpServletRequest中的方法。
- 它返回一个Cookie的数组,包含客户端随此请求发送的所有Cookie对象。
- 如果没有符合规则的cookie,则此方法返回null。
Cookie的路径限制
取自第一次访问的资源路径前缀
只要以这个前缀为开头(包括子级路径),可以获取到
反之获取不到
举例:
"/servlet/servletDemo02")(
public class ServletDemo02 extends HttpServlet{
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//创建Cookie并添加
Cookie cookie = new Cookie("username","zhangsan");
cookie.setMaxAge(3600);
resp.addCookie(cookie);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
"/servlet/servletDemo03")(
public class ServletDemo03 extends HttpServlet{
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取Cookie
Cookie[] arr = req.getCookies();
for(Cookie c : arr) {
if("username".equals(c.getName())) {
String value = c.getValue();
resp.getWriter().write(value);
}
}
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
"/servlet/aaa/servletDemo04")(
public class ServletDemo04 extends HttpServlet{
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取Cookie
Cookie[] arr = req.getCookies();
for(Cookie c : arr) {
if("username".equals(c.getName())) {
String value = c.getValue();
resp.getWriter().write(value);
}
}
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
"/bbb/servletDemo05")(
public class ServletDemo05 extends HttpServlet{
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取Cookie
Cookie[] arr = req.getCookies();
for(Cookie c : arr) {
if("username".equals(c.getName())) {
String value = c.getValue();
resp.getWriter().write(value);
}
}
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
第一次访问的路径是/servlet/servletDemo02
前缀是/servlet
/servlet/servletDemo03
和/servlet/aaa/servletDemo04
可以获取到Cookie的值
而/bbb/servletDemo05
不能获得Cookie的值,因为前缀不同
Cookie 应用:显示最后一次登陆时间
/*
Cookie的使用
*/
("/servletDemo01")
public class ServletDemo01 extends HttpServlet{
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.通过响应对象写出提示信息
resp.setContentType("text/html;charset=UTF-8");
PrintWriter pw = resp.getWriter();
pw.write("欢迎访问本网站,您的最后访问时间为:<br>");
//2.创建Cookie对象,用于记录最后访问时间
Cookie cookie = new Cookie("time",System.currentTimeMillis()+"");
//3.设置最大存活时间
//cookie.setMaxAge(3600);
cookie.setMaxAge(0); // 立即清除
//4.将cookie对象添加到客户端
resp.addCookie(cookie);
//5.获取cookie
Cookie[] arr = req.getCookies();
for(Cookie c : arr) {
if("time".equals(c.getName())) {
//6.获取cookie对象中的value,进行写出
String value = c.getValue();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
pw.write(sdf.format(new Date(Long.parseLong(value))));
}
}
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
Session概述
Cookie 在客户端浏览器保存Session标识JSESSIONID
,而每一个JSESSIONID
对应的数据存储在服务器,客户端每次发出请求时会一块把JSESSIONID
发送给服务器,服务器通过这个标识寻找对应的数据并把其响应给客户端。
当用户在应用程序的 Web页间跳转时,存储在 Session 对象中的变量不会丢失而是在整个用户会话中一直存在下去。
HttpSession 对象
它是Servlet规范中提供的一个接口。该接口的实现由Servlet规范的实现提供商提供。我们使用的是Tomcat服务器,它对Servlet规范进行了实现,所以HttpSession接口的实现由Tomcat提供。该对象用于提供一种通过多个页面请求或访问网站来标识用户并存储有关该用户的信息的方法。简单说它就是一个服务端会话对象,用于存储用户的会话数据。
同时,它也是Servlet规范中四大域对象之一的会话域对象。并且它也是用于实现数据共享的。
域对象 | 作用范围 | 使用场景 |
ServletContext | 整个应用范围 | 当前项目中需要数据共享时,可以使用此域对象。 |
ServletRequest | 当前请求范围 | 在请求或者当前请求转发时需要数据共享可以使用此域对象。 |
HttpSession | 会话返回 | 在当前会话范围中实现数据共享。它可以在多次请求中实现数据共享。 |
HttpSession 常用方法
获取HttpSession对象的方法:
public HttpSession getSession();
public HttpSession getSeesion(boolean create);
Session 使用案例
/*
Session的基本使用
*/
("/servletDemo01")
public class ServletDemo01 extends HttpServlet{
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取请求的用户名
String username = req.getParameter("username");
//2.获取HttpSession的对象
HttpSession session = req.getSession();
System.out.println(session);
System.out.println(session.getId());
//3.将用户名信息添加到共享数据中
session.setAttribute("username",username);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
/*
Session的基本使用
*/
("/servletDemo02")
public class ServletDemo02 extends HttpServlet{
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取HttpSession对象
HttpSession session = req.getSession();
System.out.println(session);
System.out.println(session.getId());
//2.获取共享数据
Object username = session.getAttribute("username");
//3.将数据响应给浏览器
resp.getWriter().write(username+"");
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
Cookie禁用时的处理方案
方案一(推荐)
直接提示Cookie被禁用了
/*
Cookie的禁用
*/
("/servletDemo03")
public class ServletDemo03 extends HttpServlet{
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取HttpSession对象
HttpSession session = req.getSession(false); //若JSESSIONID不存在,则不创建新HttpSession对象
System.out.println(session);
if(session == null) {
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().write("为了不影响正常的使用,请不要禁用浏览器的Cookie~");
}
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
方案二(重写URL)
客户端浏览器如果关闭Cookie权限,就无法把JSESSIONID传给服务器,如果想实现页面共享,就需要把JSESSIONID通过URL传递给服务器。也就是把JSESSIONID直接附加在URL路径的后面。
String encodeURL(String url)
Encodes the specified URL by including the session ID, or, if encoding is not needed, returns the URL unchanged. The implementation of this method includes the logic to determine whether the session ID needs to be encoded in the URL. For example, if the browser supports cookies, or session tracking is turned off, URL encoding is unnecessary.
"/servletDemo01")(
public class ServletDemo01 extends HttpServlet{
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取请求的用户名
String username = req.getParameter("username");
//2.获取HttpSession的对象
HttpSession session = req.getSession();
System.out.println(session);
System.out.println(session.getId());
//3.将用户名信息添加到共享数据中
session.setAttribute("username",username);
//实现url重写 相当于在地址栏后面拼接了一个jsessionid
resp.getWriter().write("<a href='"+resp.encodeURL("http://localhost:8080/session/servletDemo03")+"'>go servletDemo03</a>");
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
点击连接以后,发现URL显示http://localhost:8080/ServletDemo13;jsessionid=AEAFBD306F72BF3379A660596277E4C9
encodeURL方法使地址自动附加了JSESSIONID
HttpSession的钝化和活化
什么是持久态
把长时间不用,但还不到过期时间的HttpSession进行序列化,写到磁盘上。
我们把HttpSession持久态也叫做钝化。(与钝化相反的,我们叫活化。)
什么时候使用持久化
第一种情况:当访问量很大时,服务器会根据getLastAccessTime来进行排序,对长时间不用,但是还没到过期时间的HttpSession进行持久化。
第二种情况:当服务器进行重启的时候,为了保持客户HttpSession中的数据,也要对HttpSession进行持久化
注意
HttpSession的持久化由服务器来负责管理,我们不用关心。
只有实现了序列化接口的类才能被序列化,否则不行。