测试JAVA基础(未看)

12、Cookie & Session

2017-03-31  本文已影响128人  sunhaiyu

Cookie & Session

会话的概念

日常生活来讲,会话就是两个人聊天.。聊天的前提,聊天双方需要有记忆力.。在聊的过程中,都是基于之前聊的状态,继续往下聊。

我们JavaWeb中,浏览器和服务器也可以看作是双方在聊天(请求,响应).。浏览器服务器双方也需要有"记忆力",保存之前的聊天状态。服务器和浏览器才可以完成会话。

会话的范围

两个从打招呼到两人互相道别。是一次会话。

打开网站,完成我们想做的需求,到关闭浏览器,是一次会话。

Cookie原理

第一次访问服务器时候,服务器响应时候,要求浏览器记住一个信息。response.addCookie(cookie);

以后每次访问服务器时候,携带第一次记住的信息访问。

下面是个简单的例子,Aservlet访问服务器,让浏览器记住Cookie,再让Bservlet访问服务器,这时候可以从浏览器将Cookie取出来。

Aservlet

package cookie;

import java.io.IOException;
import java.io.PrintWriter;

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

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
//      浏览器的Response Headers里面可以看到这么一行。Set-Cookie:username=Tom
//      所以可以用如下写法
//      response.addHeader("set-Cookie", "username=Tom");
        // 当然更简单的方式是以下
        Cookie cookie = new Cookie("username", "Tom");
        response.addCookie(cookie);
    }
}

Bservlet

package cookie;

import java.io.IOException;
import java.io.PrintWriter;

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

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 获得浏览器发来的全部cookie
        Cookie[] cookies = request.getCookies();
        Cookie nameCookie = null;
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if ("username".equals(cookie.getName())) {
                    nameCookie = cookie;
                }
            }
        }
        if (nameCookie != null) {
            System.out.println(nameCookie.getName() + " : " + nameCookie.getValue());
        } else {
            System.out.println("未找到cookie");
        }
    }
}

Cookie细节

Cookie在浏览器存留时间

默认情况是关闭浏览器就会删除Cookie。如下图过期时间所示。

在发送cookie之前手动设置:

cookie.setMaxAge(60*60*24*7*2); //告诉浏览器保存2周
cookie.setMaxAge(-1); // -1代表 在会话结束时删除cookie(默认情况)
cookie.setMaxAge(0); // 通常用于删除已经存在的cookie.使用一个寿命为0的cookie,覆盖要删除的cookie

浏览器在什么情况下发送cookie(cookie的路径问题)

如果 cookie路径是"/cookie_session", 主机(域)是:localhost . 那么浏览器在访问cookie路径的所有子路径时会携带cookie。

http://localhost:8080/cookie_session/BServlet               会发送
http://localhost:8080/cookie_session/ABC/BCD/CServlet       会发送
http://localhost:8080/else/AServlet                     不会发送(项目名)
http://www.baidu.com/cookie_session/BServlet                不会发送(主机)

Cookie路径的设置

cookie的默认路径就是 发送cookie的动态资源所在的上一级路径。比如这里/cookie_session/AServlet,是Aservlet发送的,那么默认就是上一级/cookie_session

手动设置:cookie.setPath("/cookie_session/abc");

cookie的主机设置

cookie的主机(域)设置 (了解即可)

默认情况: 发送Cookie的资源所在主机。
手动设置:

//自己当前是什么主机,你就只能设置为什么主机,或者主机名的一部分
// 假设自己的主机是www.baidu.com那么可以如下设置:
c.setDomain(".baidu.com");

Cookie的删除

删除cookie原理就是 使用一个寿命为0的cookie 覆盖需要删除的cookie。c.setMaxAge(0);

覆盖cookie要求?
需要路径、键、主机 一致,即可覆盖。

使用cookie记录中文键值对问题

为什么不能直接使用中文?

因为http协议中,除正文部分都不得使用Latin码表以外的其他码表。所以不管是cookie还是之前的Cotent-disposition。都不能直接使用中文。

  1. 使用 URLEncoder 对中文进行url编码。URLEncoder.encode("中文", "UTF-8")
  2. 在获取cookie时,使用URLDecoder进行解码。URLDecoder.decode("中文", "UTF-8")

使用cookie 记录浏览历史

先做一个工具类,按填入的参数获取指定Cookie。

package cookie;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

public class CookieUtils {
    public static Cookie getCooieByName(HttpServletRequest req, String key) {
    Cookie cookie = null;
    Cookie[] cookies = req.getCookies();
    
    if (cookies != null) {
        for (Cookie c : cookies) {
            if (key.equals(c.getName())) {
                cookie = c;
            }
        }
    }
    return cookie;
    }
}
<%@ 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">
    <!--
    <link rel="stylesheet" type="text/css" href="styles.css">
    -->

  </head>
  
  <body>
    <a href="/cookie_session/Cservlet?name=dell">dell</a> <br />
    <a href="/cookie_session/Cservlet?name=lenovo">lenovo</a> <br />
    <a href="/cookie_session/Cservlet?name=hp">hp</a> <br />
    <a href="/cookie_session/Cservlet?name=acer">acer</a> <br />
    <a href="/cookie_session/Cservlet?name=apple">apple</a> <br />
    
    浏览历史:${cookie.history.value }
  </body>
</html>
package cookie;

import java.io.IOException;
import java.io.PrintWriter;

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

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
//      使用cookie 记录浏览历史.
    
//      1.获得参数,地址栏的name=lenovo等
        String name = request.getParameter("name");
//      2.获得Cookie
        Cookie history = CookieUtils.getCooieByName(request, "history");
        //存在 => 修改cookie加上现在浏览器的品牌
        if (history != null) {
          // 重复点击不会继续增加
            if (!history.getValue().contains(name)) {

            // 需要注意的是,高版本的Tomcatcookie里不支持“,”逗号。换成其他符号
                history = new Cookie("history", history.getValue()+"-"+name);
            }
        } else {
        //不存在 => 创建cookie
        history = new Cookie("history", name);
        }
//      3.将cookie发送会浏览器
        response.addCookie(history);
//      4.重定向到列表页面
        response.sendRedirect("/cookie_session/history/list.jsp");
        // /cookie_session/WebRoot/history/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 '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">
    <!--
    <link rel="stylesheet" type="text/css" href="styles.css">
    -->

  </head>
  
  <body>
  <form action="/cookie_session/Dservlet"  >
        用户名:<input type="text" name="name" value="${cookie.remember.value}" />
        <font color="red">${requestScope.error}</font>
        <br>
        密码:<input type="text" name="password" /><br>
        <input type="checkbox" name="remember" value="yes" ${cookie.remember==null?"":"checked=checked"} />记住用户名<br>
        <input type="submit" value="登录" />
    </form>
  </body>
</html>

Dservlet

package cookie;

import java.io.IOException;
import java.io.PrintWriter;

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

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
//      1.获得用户名密码
        String name = request.getParameter("username");
//      2.校验用户名密码
        if (name == null || "".equals(name.trim())) {
//          //失败=> 转发到表单页面,并提示错误
            request.setAttribute("error", "请输入用户名!");               request.getRequestDispatcher("/remember/login.jsp").forward(request, response);
            // 转发后不再继续处理逻辑
            return;
        }
        
//      3.创建cookie 添加要保存的用户名,
        Cookie c = new Cookie("remember", name);
//      4.查看记住用户名是否被选中
        String remember = request.getParameter("remember");
        //选中 => 设置保存时间为2周
        if ("yes".equals(remember)) {
            c.setMaxAge(60*60*24*7*2);
        } else {
            //没选中=>设置保存事件为0
            c.setMaxAge(0); // 清除cookie
        }

//      5.添加到响应中
        response.addCookie(c);
//      6.重定向到成功页面
        response.sendRedirect("/cookie_session/index.jsp");
    }
}

Session原理

获取session时候,request.getSession();若浏览器第一次访问,服务器会响应一个cookie给浏览器。这个cookie记录的就是sessionId,tomcat生成的sessionid叫做jsessionid。以后每次访问携带着这个sessionId,在服务器里找这个sessionId。找到后就可以获取这个sessionId里面的数据。

package session;

import java.io.IOException;
import java.io.PrintWriter;

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

public class Gservlet extends HttpServlet {
    
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        //参数类型是boolean型
        //true=> 无论如何都要获得session
        //false => 如果没有sessionID ,不会获得session
        //request.getSession(true);
        HttpSession session =  request.getSession();//相当于上面的方法 填写true
        // 上面的方法执行后,第一次访问服务器时候,服务器会响应一个cookie给浏览器。这个cookie记录的就是sessionId,tomcat生成的sessionid叫做jsessionid。
        // 比如Set-Cookie:JSESSIONID=44D210CF971936BF86AACD72DED04A82; Path=/cookie_session
        // 再次访问这个网址,携带这个cookie。如Cookie:JSESSIONID=44D210CF971936BF86AACD72DED04A82
        
    }
}

session的操作

getAttribute();
setAttribute();
removeAttribute();
getAttributeNames();

session的细节

session能在服务器端保存多久?

<session-config>
<session-timeout>30</session-timeout>
</session-config>

在tomcat/conf/web.xml 有如上配置。该配置决定了session对象的有效存活时间为30分钟。
在我们项目的web.xml中, 也可以加上如上配置.区别就是影响的范围不同。在项目中配只影响当前项目.
3.(了解)。在session对象中,还有如下方法. 该方法也是控制session对象 的有效存活时间的,单位是秒。范围是只影响调用该方法的某个session.
void setMaxInactiveInterval(int interval)

session的范围问题

浏览器第一次访问服务器,服务器创建session对象,会话开始。

三种方式销毁session

session中的其他方法

package session;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

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

public class Hservlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
    
        HttpSession session =  request.getSession();
        System.out.println("session.isNew()"+session.isNew());// 判断session是否 是新的.
        System.out.println("session.getCreationTime()"+new Date(session.getCreationTime()));//获得session的创建时间
        System.out.println("session.getId()"+session.getId());//获得session的id
        System.out.println("session.getLastAccessedTime()"+new Date(session.getLastAccessedTime()));//获得最后一次的访问时间
        System.out.println("session.getMaxInactiveInterval()"+session.getMaxInactiveInterval());// 获得session的最大有效时间
        session.setMaxInactiveInterval(60);//设置session的最大有效时间为60秒
        session.invalidate();//需要记住: 立即让session销毁。
     // 所以每次访问都会创建新的session,isNew打印true,ID也是新的,创建时间和上次访问时间也是最新的
}

验证码例子

为什么要用session域来存放验证码的正确code?

session在会话期间会一直保持着正确的code,所以session域是最合适的。

登录界面

<%@ 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 '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">
    <!--
    <link rel="stylesheet" type="text/css" href="styles.css">
    -->

  </head>
  
  <body>
  <script type="text/javascript">
  function fun1() {
  // 获得img标签对象
  var img = document.getElementById("one");
  // 改变src属性,由于Iservlet没有参数,所以这里随意填,用Date的目的是保证每次访问的唯一性
  img.src = "/cookie_session/Iservlet?date="+new Date();
  }
  </script>
  <form action="/cookie_session/Jservlet"  >
        用户名:<input type="text" name="name" value="${cookie.remember.value}" /> <br>
        密码:<input type="text" name="password" /><br>
        验证码:<input type="text" name="code" size=4 /> ![](/cookie_session/Iservlet)
        <!-- javascript:void(0)什么也不做,让fun1完成 -->
        <a href="javascript:void(0)" onclick="fun1();">看不清,换一张</a>
        <font color="red">${requestScope.error}</font>
        <br />
        <input type="checkbox" name="remember" value="yes" ${cookie.remember==null?"":"checked=checked"} />记住用户名<br>
        <input type="submit" value="登录" />
    </form>
  </body>
</html>

专门用于生成验证码的Iservlet,注意cn.dsna.util.images.ValidateCode用的是第三方包。

package session;

import java.io.IOException;
import java.io.PrintWriter;

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

import cn.dsna.util.images.ValidateCode;

public class Iservlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        // 要项浏览器正文输入时,必须指定类型
//      response.setContentType("image/jpeg");
        
        // 1. 生成验证码
        ValidateCode code = new ValidateCode(120, 40, 4, 2);
        // 2. 将正确答案放进Session
        String correctCode = code.getCode();
        request.getSession().setAttribute("code", correctCode);
        // 3. 将图片发送给浏览器
        System.out.println(correctCode);
        code.write(response.getOutputStream());
    }

}

点击登录跳转到Jservlet,取出用户输入的code和session域存放的正确code比较。

package session;

import java.io.IOException;
import java.io.PrintWriter;

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

public class Jservlet extends HttpServlet {

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

        // 1. 获得用户填写的验证码
        String userCode = request.getParameter("code");
        // 2.  获得session中的正确验证码
        String correctCode = (String) request.getSession().getAttribute("code");
        // 3. 比较两者
        if (userCode != null && userCode.equalsIgnoreCase(correctCode)) {
            // 验证成功,重定向到登录成功的页面
            response.sendRedirect("/cookie_session/index.jsp");
        } else {
            request.setAttribute("error", "请输入正确的验证码!");
            request.getRequestDispatcher("/login.jsp").forward(request, response);
        }
    }
}

如上图,验证码填错了会转发到登录界面。

注意是转发过去是一次请求,转发过去还是刚才的jsp页面。${requestScope.error}才能收到request.setAttribute("error", "请输入正确的验证码!");的错误信息;如果是重定向又重新请求了,重新加载jsp,不会显示错误信息。

购物车例子

商城商品页面。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 'index.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">
    <!--
    <link rel="stylesheet" type="text/css" href="styles.css">
    -->
  </head>
  
  <body>
    <h1>商品列表</h1>
    <table border="1" >
        <tr>
            <td>
                肥皂<br>
                <a href="/cookie_session/Kservlet?name=0"  >点击加入购物车</a>
            </td>
            <td>
                蜡烛<br>
                <a href="/cookie_session/Kservlet?name=1" >点击加入购物车</a>
            </td>
        </tr>
        <tr>
            <td>
                被罩<br>
                <a href="/cookie_session/Kservlet?name=2" >点击加入购物车</a>
            </td>
            <td>
                枕头<br>
                <a href="/cookie_session/Kservlet?name=3" >点击加入购物车</a>
            </td>
        </tr>
    </table>
        <a href="/cookie_session/cart.jsp" >查看购物车</a>
  </body>
</html>

购物车详情,cart.jsp

<%@page import="java.util.Map.Entry"%>
<%@ 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 'cart.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">
    <!--
    <link rel="stylesheet" type="text/css" href="styles.css">
    -->

  </head>
  
  <body>
    <h1>购物车</h1>
    <table border="1">
        <tr>
            <th>商品名称</th>
            <th>商品数量</th>
        </tr>
        <%
            Map<String,Integer> cart = (Map<String,Integer>)session.getAttribute("cart");
            if(cart!=null && cart.size()>0){
                for(Entry<String,Integer> en : cart.entrySet()){
                %>
                <tr>
                    <td><%=en.getKey() %></td>
                    <td><%=en.getValue() %></td>
                </tr>
                <% }
            }
         %>
    </table>
  </body>
</html>

Kservlet

package session;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.LinkedHashMap;
import java.util.Map;

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

public class Kservlet extends HttpServlet {

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

       //0 准备数组,与页面商品对应
       String[] products = {"肥皂","蜡烛","被罩","枕头"};
               
//      1. 获得当前请求要添加的商品
       String indexStr = request.getParameter("name");// 0 ~ 3 
               
       String productName = products[Integer.parseInt(indexStr)];
//      2.从Session中取出存放购物车的map
       Map<String,Integer> cart = (Map<String, Integer>) request.getSession().getAttribute("cart");
       if(cart==null){
//      //取不到=>初始化map,LinkedHashMap保证存入和取出的顺序相同,因为HashMap是无序的
       cart = new LinkedHashMap<String, Integer>();
       //并放入session
       request.getSession().setAttribute("cart", cart);
       }
//      3.使用当前要添加的商品从map中取值,默认数量为1
       Integer count = cart.put(productName, 1);
       // 不为空说明值被覆盖,加一。为空说明上面设置为1是正确的
       if(count!=null){
           cart.put(productName, count+1);
       }
       //取不到=> 放入map,设置数量为1
   //取得到=> 将数量+1放回去
//      4.重定向回商品列表页面
       response.sendRedirect("/cookie_session/list.jsp");
   }
}

注意:后退或者刷新,再次选择物品、购物车依然会累计。但是当浏览器关闭后,再次访问购物车就清零了。因为cookie被销毁,sessionId已经没有了,再去服务器找的时候是没有携带的,需要新建。

Cookie和Session的区别

因此,维持一个会话的核心就是客户端的唯一标识,即 sessionId


by @sunhaiyu

2017.3.31

上一篇下一篇

猜你喜欢

热点阅读