0%

Session&Cookie

会话

什么是会话

会话可以简单理解为:用户打开一个浏览器,点击多个超链接访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话。

会话过程中要解决的一些问题

每个用户在使用浏览器与服务器进行会话的过程中,不可避免各自回产生一些数据,成需要想办法为每个用户保存这些数据。

保存会话的两种技术:

Cookie是客户端技术,程序把每个用户的数据以cookie的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去。这样,web资源处理的就是用户各自的数据了。

Session

Session是服务器端的技术,利用这个技术,服务器在运行时可以为每一个用户的浏览器创建一的其独享的HttpSession对象。由于Session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的Session中。当用户再去访问服务器中其他的web资源时,其他的web资源再从用户各自的Session中取出数据为用户服务。

  • javax.servlet.http.Cookie类用于创建一个Cookie。
  • response接口中定义了一个addCookie的方法,它用户在其响应头中增加一个相应的Set-Cookie头字段。
  • request接口中也定义了一个getCookies方法,它用于获取客户端提交的Cookie。

Cookie应用场景

记录上次访问时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@WebServlet("/Servlet01")
public class Servlet01 extends HttpServlet{
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求头的Cookie
Cookies[] cks = req.getCookies();
for(Cookies c : cks){
System.out.println(c.getName() + ":" + c.getValue());
}

//创建一个Cookie对象
Cookie ck = new Cookie("lastAccessTime", System.currentTimeMillis()+"");

//设置Cookie的存活时间(单位是秒)
ck.setMaxAge(60 * 5);

//把Cookie返回给客户端(通过响应头传给客户端)
resp.addCookie(ck);
}

Cookie的maxAge方法

maxAge():cookie的缓存时间。默认是-1,默认存在浏览器的缓存中。单位是秒。

  • 负数:表示cookie的数据存在浏览器缓存中。
  • 0:表示删除cookie。
  • 正数:缓存在持久化硬盘上的时间。

Cookie细节

  • 一个cookie只能标识一种信息,它至少含有一个标识该信息的名称(name)和设置值(value)。
  • 一个web站点可以给一个web浏览器发送多个cookie,一个web浏览器也可以存储多个web站点提供的cookie。
  • 浏览器一般只允许存放300个cookie,每个站点最多存放20个cookie,每个cookie的大小限制为4KB。
  • 如果创建了一个cookie,并把它发送到浏览器,默认情况下他是一个会话级别的cookie(即存储在浏览器的内存中),用户退出浏览器之后即被删除。若希望浏览器将该cookie存储在磁盘上,则须使用maxAge,并给出一个以秒为单位的时间。将最大时效设置为0则是命令浏览器删除该cookie。

案例:记住用户名

login.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>login</title>

<script>
function getCookieValue(key){
//通过js获取cookie
var cookie = document.cookie;//"remember=true; username=17103202"
//拆分cookie
var cks = cookie.split("; ");
for(var i=0; i<cks.length; i++){
//取name和value
var ckArr = cks[i].split("=");
if(key == ckArr[0]){
return ckArr[1];
}
}
}

window.onload = function(){
var remember = getCookieValue("remember");
var username = getCookieValue("username");
if(remember == "true"){
//勾选记住用户名
var rememberTag = document.getElementById("remember");
rememberTag.checked = "checked";

//给用户名显示上次登陆的名字
var usernameTag = document.getElementById("username");
usernameTag.value = username;
}
}

</script>

</head>
<body>
<form action="/RememberLogin/Servlet01" method="post">
<table border="1">
<tr>
<td>用户名</td>
<td><input id="username" type="text" name="username"></td>
</tr>
<tr>
<td>密码</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td colspan="2">
<input id="remember" type="checkbox" name="remember" value="true">记住用户名
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="登陆">
</td>
</tr>
</table>
</form>
</body>
</html>

RemLogin.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package com.Retur0.web.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/Servlet01")
public class RemLogin extends HttpServlet{
private static final long serialVersionUID = 1L;

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("content-type", "text/html;charset=utf-8");

//获取表单请求参数
String username = req.getParameter("username");
String password = req.getParameter("password");
String remember = req.getParameter("remember");

//简单判断是否登陆成功
boolean success = "17103202".equals(username) && "111".equals(password);
if(success) {
resp.getWriter().write("登陆成功");
}else {
resp.getWriter().write("登陆失败");
//清除cookie
Cookie[] cks = req.getCookies();
if(cks!=null) {
for(Cookie ck : cks) {
ck.setMaxAge(0);//删除cookie
resp.addCookie(ck);
}
}
}

//把remember和username存在cookie中
if("true".equals(remember) && success) {
Cookie ck1 = new Cookie("remember", remember);
ck1.setMaxAge(60 * 60);
resp.addCookie(ck1);

Cookie ck2 = new Cookie("username", username);
ck1.setMaxAge(60 * 60);
resp.addCookie(ck2);
}
}
}

历史记录

查看书的浏览记录

客户端:Cookie:historyBookIds=1-3-5

原理

在访问过条目1之后将条目1的id存到cookie中反回给客户端,客户端访问条目2的时候将这个cookie发给服务器,并把条目2的id也存进cookie中。如果客户端要显示历史记录,则通过cookie中记载的id信息调出相应的条目信息并显示在浏览器上。在cookie中id通过键值对以字符串的形式进行保存。

Session API

  • 服务器可以为浏览器创建一个session对象,一个浏览器独占一个session。在需要保存用户数据时,可以把用户数据写道浏览器独占的session中。当用户浏览器访问其他程序时,替他程序可以从用户的session中取出用户的数据,为用户服务。
  • session和cookie的区别在于:
    • cookie是把用户的数据写给用户浏览器。
    • session是把用户的数据写道用户独占的session中。
  • session对象由服务器创建,开发人员可以调用request对象的getSession方法得到session对象。

举个例子

SessionDemo1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@WebServlet("/SessionDemo1")
public class SessionDemo1 extends HttpServlet{
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取session对象
HttpSession session = req.getSession();

//存数据到session
session.setAttribute("name", "Retur0");

//转发到sessiondemo2
resp.sendRedirect(req.getContextPath() + "/SessionDemo2");
}
}

SessionDemo2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@WebServlet("/SessionDemo2")
public class SessionDemo2 extends HttpServlet{
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取session对象
HttpSession session = req.getSession();

//从session取数据
String name = (String)session.getAttribute("name");

//数据显示在客户端上
resp.getWriter().write(name);
}
}

结果:在访问SessionDemo1时会跳转到SessionDemo2并显示Retur0

如果访问SessionDemo1和SessionDemo2的浏览器不同,则无法显示出Retur0

关闭浏览器再打开,session是不同的。

服务器如何实现一个session服务一个用户浏览器

服务器会为每个浏览器分配一个session ID,然后把Session ID通过Cookie的形式存储在客户端。

  1. 第一次访问session1时,服务器创建一个session对象,jsessionid在响应头cookie中发送给浏览器并保存。此时如果再次访问session1,jsessionid不会再发给浏览器。

  2. 访问seesion2会将jsessionid在请求头中发送给服务器,服务器用这个id找到对应的session读取数据。

Session的应用场景

购物车

  • 用户买书-书放在购物车

    1. 获取书的id

      String bookid = req.getParameter("id");

    2. 通过书的id找到这本书

      Book book = DBUtils.findBookById(Integer.parseInt(bookId));

    3. 把这本书放在购物车List<Book> cart

      1
      2
      3
      4
      5
      6
      List<Book> cart = (List<Book>)req.getSession().getAttribute("cart");
      //如果第一次没有创建过购物车,不加判断会重复创建新cart,cart中没有以前添加的东西。
      if(cart == null){
      cart = new ArrayList<Book>();
      }
      cart.add(book);
    4. 把这个cart放入session中

      req.getSession().setAttribute("cart", cart);

  • 这种方法的问题:

    1. 重启浏览器数据会丢失。(解决:把书的id放在cookie中)
    2. 重启服务器数据会丢失。(解决:1. 把书的id放在cookie中。2. 把session数据保存起来)

验证码

在登陆过程中,生成验证码和验证登录在不同的servlet中,验证码数据在ValidateServet中,LoginServlet中只能获得用户输入的验证码,为了检验这两个码是否相同,需要使用session来共享数据。

将ValidateServlet中的码存入到session中,LoginServlet从session中取出码并和用户输入的码进行比较。

  • 忽略大小写的字符串比较可以使用equalsIgnoreCase()方法。

  • 使用equals方法是一般将字符串放在前面,变量作为参数,这样可以避免空指针异常。

  • 验证结束后需要将验证码服务器中删除。

    req.getSession().removeAttribute("code");

    或者使用invalidate方法,这个方法让session失效,会使session中的所有数据都删除。

HttpSession的常用方法

  • 把数据保存在HttpSession中,该对象也是一个域对象。
  • void setAttribute(String name, Object value); 存数据。
  • Object getAttribute(String name); 取数据。
  • void removeAttribute(String name); 通过key删除数据。
  • HttpSession.getId() 取session的id。
  • setMaxInactiveInterval(int interval); 设置session的存活时间。
  • invalidate; 使此会话无效。

getSession内部执行原理

  1. 获取名称为JSESSIONID的Cookie的值。

  2. 如果没有这样的cookie,创建一个新的HttpSession对象,分配一个唯一的SessionID,并且向客户端写了一个名字为JSESSIONID = sessionid的cookie。

  3. 有这样的cookie,获取cookie中JSESSIONID的值,在服务器内存中根据ID找到那个HttpSession对象。

  4. 找到了就取出来使用,找不到从2开始。

    • 使用getSession时可以传进一个布尔值参数,这个参数是true就正常使用。

      这个参数是false则根据id查找session对象,找到就返回,找不到返回null,不会创建新的。

Session的状态

  • 创建:当浏览器第一次访问浏览器的动态资源就被创建。

  • 活着:服务器运行时。

  • 死亡:

    1. session.invalidate():强制销毁。

    2. 超时:默认30分钟。

    3. setMaxInactiveInterval(int):设置存活时间,秒。

    4. 在web.xml中配置session有效时间。

      1
      2
      3
      4
      <session-config>
      <!--30分钟有效时间-->
      <session-timeout>30</session-timeout>
      </session-config>

Session的持久化

确保服务器或单个web应用重启后,能恢复重启前的会话。

把session中存的对象实现Serializable接口。若不实现序列化接口,则在重启服务器的时候会出现session无法序列化的警告。

会话持久化在硬盘中保存的路径是:/work/Catalina/localhost/项目名/SESSION.ser。

持久化状态转化

Session在其生命周期中,可能会在运行时状态和持久化状态之间转换。

  • 搁置:

    会话从运行时状态转化为持久化状态的过程称为搁置。

    以下情况发生时,Session会被搁置:

    1. 当服务器重启或单个web应用终止时,web应用中的session会被搁置。
    2. 会话处于不活动的时间太长,达到了特定值。
    3. web应用中处于运行状态的会话数目太多部分Session被搁置。
  • 激活:

    会话从持久化状态变为运行时状态的过程称为激活。

    以下情况发生时,Session会被激活:

    1. 当服务器重启或单个web应用重启时,web应用中的Session会被激活。
    2. 处于Session中的客户端向web应用发出http请求,相应的Session会被激活。