Cookie & Session

Cookie 和 Session 都是会话技术的一种,因为http协议是无状态的,每次都是基于一个请求和一个响应,新的请求和响应都与之前请求和响应没有关系,因此在客户端(浏览器)使用Cookie技术保存之前的对话信息。服务器要求浏览器保存一些键值对,浏览器在适当的时机就会发送给服务器。

  • Cookie
    • Usage
    • Notes
    • Application
  • Session
    • Notes
    • Usages
    • Application

Cookie是客户端保存会话信息的技术

Usage

服务器使用response发送响应头让浏览器记住键值对,服务器保存cookie的响应头:Set-Cookie:name=tom;

response.addHeader("Set-Cookie","name=tom");
response.addHeader("Set-Cookie","sex=male");

当浏览器请求服务器时,会把该服务器保存的Cookie随请求发送给服务器,即在请求头中添加以下信息:Cookie: name=tom; sex=male,多个键值对间用分号隔开。

  • 原始方式

    • 使用response发送Set-Cookie响应头
    • 使用request获取请求头
  • 便捷方式

    • 使用response.addCookie()方法向浏览器保存Cookie
    • 使用request.getCookies()方法获取浏览器归还的Cookie,返回的对象为Cookie数组,若没有则返回nul
  • 添加Cookie

package cookie;

import java.io.IOException;

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

public class CookieAdd extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
                //向浏览器保存
            Cookie cookie = new Cookie("name", "tom");
            response.addCookie(cookie);
            response.getWriter().print("send cookie successed!");
    }
}
  • 获取cookie
package cookie;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class CookieGet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //获取浏览器归还的cookie   
        Cookie[] cookies  = request.getCookies();
        //遍历并判断我们要找的cookie
        if(cookies!=null && cookies.length>0){
            for(Cookie c : cookies){
                    if(c.getName().equals("name")){
                        System.out.println("获得的cookie:"+c.getName()+":"+c.getValue());
                    }
            }
        }
    }
}

Notes

  • 1个Cookie最大4kb;
  • 1个服务器最多向一个浏览器保存20个Cookie;
  • 1个浏览器最多保存300个Cookie。
  • 浏览器保存Cookie时间:默认是在会话期间有效(关闭浏览器,cookie就被删除)(即设置有效时间为-1)
  • 有效时间如何设置?
    • 设置最大有效时间:Cookie.setMaxAge(int);
    • 设置一个正数,表示最大有效时间,单位是秒(保存到硬盘中);
    • 设置为-1,就是相当于默认有效时间, 浏览器关闭就消失(cookie保存在浏览器内存中);
    • 设置为0,发送到浏览器就消失了。利用有效时间为0,我们可以做删除cookie的操作,把之前的cookie覆盖,覆盖要保证cookie的路径和键相同,因为同一个路径,不能存在相同的cookie(键相同),我们可以通过覆盖的方式,设置有效时间为0,删除cookie。
  • 浏览器在什么情况下发送cookie(路径)
    • Cookie如果不设置路径,默认路径取当前路径,Cookie的默认路径就是发送Cookie的servlet所在目录/cookiedemo/abc/xxxServlet;
    • cookie 会把键值对带给自己路径下的所有子路径;
    • 访问路径如果是cookie路径的子路径,那么浏览器就会把该cookie告诉服务器.
    • 设置给某个具体servlet使用:

示例:

package cookie.c_OtherDetails;

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 CookiePath extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        Cookie cookie = new Cookie("haha", "gaga");
        cookie.setPath("/Cookie/CookiePath");
        response.addCookie(cookie);
    }
}
package cookie.c_OtherDetails;

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 CookieTime extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        Cookie cookie = new Cookie("age", "18");
        // 设置cookie的最大有效时间
        // cookie.setMaxAge(60*60);

        // 设置为-1 , 就是相当于默认有效时间, 浏览器关闭就消失.
        // cookie.setMaxAge(-1);

        // 设置cookie的有效时间为0则发送到浏览器就消失。
        // 使用此方式可以做删除cookie的操作,因为同一个路径,不能存在相同的cookie(键相同),因此可以通过覆盖的方式,设置有效时间为0从而删除cookie
        // cookie.setMaxAge(0);
        response.addCookie(cookie);
    }
}
  • 了解cookie中的域:跨主机访问cookie(不常用):想要以下三个主机和主机下的项目能共享一个cookie:www.baidu.com、 music.baidu.com、map.baidu.com,完成两步即可:
    1. 设置cookie的domain域为 “.baidu.com”;
    2. 设置cookie路径 为: “/”

Application

  • 服务器使用Cookie来跟踪客户端状态;
  • 保存购物车

购物车中商品不能使用request域来保存,因为它是一个用户向服务器发送的多个请求信息

  • 显示上次登录的用户名

login.jsp

<%@ page language="java" pageEncoding="UTF-8" %>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
    <base href="<%=basePath%>">

    <title>My JSP 'login.jsp' starting page</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="This is my page">
</head>

<%
    //1.获得浏览器发送 过来的cookie
    Cookie[] cookies = request.getCookies();
    //2.遍历找到保存用户名的
    Cookie remember = null;
    if (cookies != null && cookies.length > 0) {
        for (Cookie c : cookies) {
            //3.如果找到取得它的值,把值设置到用户名所在input
            if (c.getName().equals("remember")) {
                remember = c;
            }
        }
    }
%>

<body>
<form action="/cookie/RememberUserInfo" method="post">
    用户名: <input type="text" name="name" value="<%=remember==null?"":remember.getValue() %>"/>
    密码: <input type="password" name="password"/><br/>
    记住用户名(一周):<input type="checkbox" name="remember" value="yes" <%=remember == null ? "" : "checked" %> /><br/>
    <input type="submit" value="登录"/>
</form>
</body>
</html>

package cookie;

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 RememberUserInfo extends HttpServlet {

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 1.获得用户并名新建cookie
        String name = request.getParameter("name");
        String remember = request.getParameter("remeber");
        Cookie cookie = new Cookie("remember", name);
        // 2.判断remember
        if (remember != null && remember.equals("yes")) {
            // "yes"(勾选了) - 设置有效时间为一周
            cookie.setMaxAge(60 * 60 * 24 * 7);
        } else {
            // null(没勾选) - 设置cookie的有效时间为0
            cookie.setMaxAge(0);
        }
        // 3.将cookie添加到response
        response.addCookie(cookie);
        // 4.跳转到成功页面
        request.getRequestDispatcher("/index.jsp").forward(request, response);
    }
}

  • 历史记录

list.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    <title>My JSP 'list.jsp' starting page</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="This is my page">
  </head>
  
  <body>
        <a href="<%=request.getContextPath() %>/HistoryServlet?name=dell" >dell</a> <br>
        <a href="<%=request.getContextPath() %>/HistoryServlet?name=lenovo" >lenovo</a><br>
        <a href="<%=request.getContextPath() %>/HistoryServlet?name=apple" >apple</a><br>
        <a href="<%=request.getContextPath() %>/HistoryServlet?name=acer" >acer</a><br>
        <a href="<%=request.getContextPath() %>/HistoryServlet?name=hasee" >hasee</a><br>
        浏览历史:
        <%=request.getAttribute("history") %>
  </body>
</html>
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 HistoryServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 1.获得当前访问的品牌
        String current = request.getParameter("name");
        // 2.获得之前访问过的品牌(获得cookie)
        Cookie history = null;
        Cookie[] cookies = request.getCookies();
        if (cookies != null && cookies.length > 0) {
            for (Cookie c : cookies) {
                if (c.getName().equals("History")) {
                    history = c;
                }
            }
        }
        // 3.将当前访问加上之前访问的,创建一个新的cookie.
        // "history" = "dell,apple,hasee"
        String value = "";
        if (history != null) {
            value = history.getValue();
            if (!history.getValue().contains(current)) { //判断当前访问的是否之前已经访问过
                value += "," + current;
            }
        } else {//没有获得到之前的cookie,说明是第一次访问.
            value = current;
        }
        Cookie newHistory = new Cookie("History", value);

        // 4.将cookie添加到响应中
        response.addCookie(newHistory);
        // 5.将商品记录 放置到request域中
        request.setAttribute("history", value);
        // 6.转发到jsp显示
        request.getRequestDispatcher("/history/list.jsp").forward(request, response);
    }
}

Session

Session是服务器端保存会话信息的技术,底层依赖Cookie,或是URL重写。

HttpSession是服务器端对象,保存在服务器端,是三大域对象(request、session、application)之一,因此包含域操作相关方法。

原理: 每当客户端与服务器端建立一个会话,服务器就会该客户端建立一个Session对象,将Session对象保存在服务器端的同时服务器还会给客户端发送一个带有JSESSIONID的Cookie(放在响应头中);
当客户端在不关闭的情况下再次访问该服务器时,客户端就会通过这个JSESSIONID来取出保存在服务器端相应的Session对象;
当客户关闭了浏览器时,意味着与服务器段段开连接然后会话session也就随之结束了,此时能识别存在于服务器端的HttpSession对象的JSESSIONID也随之丢失了,但此时HttpSession对象依旧保存在服务器端,并且只存在于他的生命周期时间内,默认30分钟;因此即使不关闭浏览器,长时间不发出请求,服务器端也会删除。

Notes

  • 会话范围:session标示一次会话,会话:一个用户对服务器的多次连贯性请求!所谓连贯请求,就是该用户多次请求之间没有关闭浏览器。
    • 开始:第一次访问服务器
    • 结束:服务器让浏览器记住sessionID的cookie默认过期时间是(设置有效时间为-1) –> 关闭浏览器 cookie就丢失 –> cookie丢失,sessionID就丢失 –>找不到服务器的session
  • 客户端与服务器端建立连接,服务器端不会立刻建立HttpSession对象,因为HttpSession存活的时间较长,所有只有当客户端请求的Servlet中调用了request.getSession()方法要求获取时服务器才会创建;
  • 若请求的是jsp页面,则服务器会立刻创建HttpSession,因为session是jsp内置对象之一,jsp对应的servlet类中已经默认调用了request.getSession();
  • 关于设置session的最大有效时间
    • 默认是30分钟,在tomcat的web.xml中<session-config>配置的.
    • 如何修改session的过期时间?
      1. 修改在tomcat的web.xml<session-config> - 影响服务器中的所有项目
      2. 在项目的web.xml中 加入<session-config>配置 - 影响的是当前项目
      3. 通过setMaxInactiveInterval(int interval)方法设置 - 当前操作的session
  • 调用request.getSession()方法后服务器:获取Cookie中的JSESSIONID,需要判断JSESSIONID是否存在
    • 不存在,则创建session,保存session并把新创建的JSESSIONID保存到Cookie中;
    • 存在,通过JSESSIONID查找session对象
      • 如果没有查找到,则创建session并保存起来,把新创建的JSESSIONID保存到Cookie中;
      • 如果查找到了,那么就不会在创建session对象了;
  • session不能跨项目
  • session在一个项目中可以对项目中的任意路径共享数据,因为保存sessionID的cookie的路径是项目路径

URL重写(了解)

<a href="/SessionProject/AServlet;jsessionid=<%=request.getSession().getId() %>">AServlet</a>
<a href="<%=response.encodeURL("/SessionProject/AServlet")%>">AServlet</a>

如果浏览器禁用cookie功能不能保存任何cookie,那么session技术要是用cookie来保存sessionID,没有cookie怎么保存?
使用url重写解决该问题,将页面中所有的连接末尾全都加上cookieid的参数,这样用户点击连接访问网站,通过url把SeesionID带到了服务器,这样就解决了,但是互联网行业没有这么干的。

Usages

// 获得session域
HttpSession session = request.getSession();

// 操作Session域
// 域相关方法
session.setAttribute(arg0, arg1);
session.getAttribute(arg0);
session.removeAttribute(arg0); 
session.getAttributeNames(); 

// 其他方法
long getCreationTime(); // 获得创建时间
String getId(); // 获得sessionID        
long getLastAccessedTime(); // 获得最后一次访问时间       
int  getMaxInactiveInterval(); // 获得session的寿命        
void setMaxInactiveInterval(int interval); // 设置session的过期时间         
void invalidate(); // 让session立即失效  
// 查看当前获得的session是否是新的(只有在第一访问服务器,session是新的 )
boolean isNew();   

Application

应用一:验证码的实现

添加ValidateCode.jar
login.jsp

<%@ page language="java" pageEncoding="UTF-8" %>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
    <base href="<%=basePath%>">

    <title>My JSP 'login.jsp' starting page</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="This is my page">

    <script type="text/javascript">
        function change() {
            // 1.获得img对象
            var img = document.getElementById("one");
            // 2.改变img对象src属性
            img.src = "/Session/CookiePath?abc=" + new Date();
        }
    </script>
</head>

<body>
<form action="/Session/SubmitServlet" method="post">
    用户名: <input type="text" name="name"/><br>
    密码: <input type="password" name="password"/><br>
    验证码: <input type="text" name="verificationCode"/><img src="/Session/VerificationCodeServlet" width="150" id="one"
                                                          onclick="change();"/>
    <a href="javaScript:void(0)" onclick="change();">看不清换一张</a><br>
    <input type="submit" value="登录"/> <br>
</form>
<font color="red">
    <%=request.getAttribute("error") == null ? "" : request.getAttribute("error") %>
</font>
</body>
</html>
import cn.dsna.util.images.ValidateCode;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class VerificationCodeServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 1.生成验证码
        ValidateCode code = new ValidateCode(200, 80, 4, 100);
        // 2.将验证码保存到session中
        System.out.println(code.getCode());
        request.getSession().setAttribute("code", code.getCode());
        // 3.将验证码图片输出到浏览器
        response.setContentType("image/jpeg");
        code.write(response.getOutputStream());
    }
}
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class SubmitServlet extends HttpServlet {

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 1.获得表单提交的验证码
        String code1 = request.getParameter("verificationCode");
        // 2.获得session中的正确验证码
        String code2 = (String) request.getSession().getAttribute("code");
        // 3.比对是否一致
        if (code1 != null && code2 != null && code1.equals(code2)) {
            //正确:成功页面
            response.sendRedirect("/Session/index.jsp");
        } else {
            //不正确:回到表单 页面 ,提示错误
            request.setAttribute("error", "验证码错误!请重新输入");
            request.getRequestDispatcher("/VerificationCode/login.jsp").forward(request, response);
        }
    }
}

应用二:shopping cart list.jsp

<%@ page language="java" pageEncoding="UTF-8" %>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
    <base href="<%=basePath%>">
    <title>My JSP 'list.jsp' starting page</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="This is my page">
</head>
<body>
<br>
肥皂<a href="/session/ShoppingCart?name=0">加入购物车</a><br/>
电动车<a href="/session/ShoppingCart?name=1">加入购物车</a><br/>
笔记本<a href="/session/ShoppingCart?name=2">加入购物车</a><br/>
小雨伞<a href="/session/ShoppingCart?name=3">加入购物车</a><br/>
八度空间<a href="/session/ShoppingCart?name=4">加入购物车</a><br/>
床单<a href="/session/ShoppingCart?name=5">加入购物车</a><br/>
<a href="/session/shoppingcart/shoppingcart.jsp">查看购物车</a>
</body>
</html>
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.LinkedHashMap;
import java.util.Map;

public class ShoppingCart extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 1.获得要添加的商品(0~5)
        String name = request.getParameter("name");
        int intName = Integer.parseInt(name);
        // 2.将数字翻译成中文商品名称 (使用数组)
        String[] products = new String[]{"肥皂", "电动车", "笔记本", "小雨伞", "八度空间", "床单"};
        String productName = products[intName];
        // 3.获得session
        HttpSession session = request.getSession();
        // 4.获得session中保存的购物车(Map)
        Map<String, Integer> map = (Map<String, Integer>) session.getAttribute("cart");
        if (map != null) {
            //获得到了:不是第一次访问==>继续向Map中添加
            Integer count = map.get(productName);
            if (count == null) {
                //不存在:添加并设置数量为1
                count = 1;
            } else {
                //map中已经存在这个商品:把数量加1
                count++;
            }
            map.put(productName, count);
        } else {
            //没获得到:第一次访问
            map = new LinkedHashMap<String, Integer>();
            //新建Map:将商品添加并设置数量为1
            map.put(productName, 1);
        }
        // 5.将Map操作完再放回session
        session.setAttribute("cart", map);
        // 6.返回到列表页面(重定向)
        response.sendRedirect(request.getContextPath() + "/shoppingcart/list.jsp");
    }
}

shoppingcart.jsp

<%@page import="java.util.Map" %>
<%@ page language="java" import="java.util.Map.Entry" pageEncoding="UTF-8" %>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
    <base href="<%=basePath%>">
    <title>My JSP 'list.jsp' starting page</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="This is my page">
</head>
<%
    //1.获得session
    //2.从session中取出购物车的map
    Map<String, Integer> map = (Map<String, Integer>) session.getAttribute("cart");
%>
<body>
<h1 align="center">购物车详情</h1>
<table border="1" align="center">
    <tr>
        <th>商品名称</th>
        <th>商品数量</th>
    </tr>
    <%
        //3 遍历显示
        if (map != null && map.size() > 0) {
            for (Entry<String, Integer> en : map.entrySet()) {
    %>
    <tr>
        <td><%=en.getKey() %></td>
        <td><%=en.getValue() %></td>
    </tr>
    <%
            }
        } else {
            out.print("您还没有添加任何商品!!");
        }
    %>
</table>
</body>
</html>