javaWeb基础知识----Cookie,Session

    • HTTP 协议是一种无状态的协议 WEB 服务器本身不能识别出哪些请求是同一个浏览器发出的,浏览器的每一次请求都是完全孤立的
    • 即使 HTTP1.1 支持持续连接,但当用户有一段时间没有提交请求,连接也会关闭。
    • 怎么才能实现网上商店中的购物车呢:某个用户从网站的登录页面登入后,再进入购物页面购物时,负责处理购物请求的服务器程序必须知道处理上一次请求的程序所得到的用户信息。
    • 作为 web 服务器,必须能够采用一种机制来唯一地标识一个用户,同时记录该用户的状态
1.会话和会话状态
    • WEB 应用中的会话是指一个客户端浏览器与 WEB 服务器之间连续发生的一系列请求和响应过程。
    • WEB 应用的会话状态是指 WEB 服务器与浏览器在会话过程中产生的状态信息, 借助会话状态, WEB 服务器能够把属于同一会话中的一系列的请求和响应过程关联起来
2.如何实现有状态的会话
    • WEB 服务器端程序要能从大量的请求消息中区分出哪些请求消息属于同一个会话,即能识别出来自同一个浏览器的访问请求,这 需要浏览器对其发出的每个请求消息都进行标识 :属于同一个会话中的请求消息都附带同样的标识号,而属于不同会话的请求消息总是附带不同的标识号,这个标识号就称之为会话 ID SessionID )。
    • Servlet 规范中,常用以下两种机制完成会话跟踪
    – Cookie
    – Session

一、Cookie


/**
 * @author changwen on 2016/11/13.
 */
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.ResourceBundle;

public class Cookie implements Cloneable, Serializable {
    private static final long serialVersionUID = -6454587001725327448L;
    private static final String TSPECIALS;
    private static final String LSTRING_FILE = "javax.servlet.http.LocalStrings";
    private static ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.http.LocalStrings");


    private String name;  //这个只有get方法
    /*
    下面几个属性都只有简单get,set方法
     */
    private String value;
    private String comment;
    private String domain;  //这个set方法有处理过
    private int maxAge = -1;
    private String path;
    private boolean secure;
    private int version = 0;
    //需要了解,boolean类型的get方法是这样子public boolean isHttpOnly() {return isHttpOnly;}
    private boolean isHttpOnly = false;


    public Cookie(String name, String value) {
        if(name != null && name.length() != 0) {
            if(this.isToken(name) && !name.equalsIgnoreCase("Comment") && !name.equalsIgnoreCase("Discard")
                    && !name.equalsIgnoreCase("Domain") && !name.equalsIgnoreCase("Expires")
                    && !name.equalsIgnoreCase("Max-Age") && !name.equalsIgnoreCase("Path")
                    && !name.equalsIgnoreCase("Secure") && !name.equalsIgnoreCase("Version")
                    && !name.startsWith("$")) {
                this.name = name;
                this.value = value;
            } else {
                String errMsg = lStrings.getString("err.cookie_name_is_token");
                Object[] errArgs = new Object[]{name};
                errMsg = MessageFormat.format(errMsg, errArgs);
                throw new IllegalArgumentException(errMsg);
            }
        } else {
            throw new IllegalArgumentException(lStrings.getString("err.cookie_name_blank"));
        }
    }


    private boolean isToken(String value) {
        int len = value.length();

        for(int i = 0; i < len; ++i) {
            char c = value.charAt(i);
            if(c < 32 || c >= 127 || TSPECIALS.indexOf(c) != -1) {
                return false;
            }
        }

        return true;
    }

    public Object clone() {
        try {
            return super.clone();
        } catch (CloneNotSupportedException var2) {
            throw new RuntimeException(var2.getMessage());
        }
    }


    static {
        if(Boolean.valueOf(System.getProperty("org.glassfish.web.rfc2109_cookie_names_enforced", "true")).booleanValue()) {
            TSPECIALS = "/()<>@,;:\\\"[]?={} \t";
        } else {
            TSPECIALS = ",; ";
        }

    }

    public void setDomain(String domain) {
        this.domain = domain.toLowerCase(Locale.ENGLISH);
    }
    public String getDomain() {
        return this.domain;
    }

    public String getName() {
        return this.name;
    }


}

Servlet API 中提供了一个 javax.servlet.http.Cookie 类来封装 Cookie 信息,它包含有生成 Cookie 信息和提取 Cookie 信息的各个属性的方法。
    • HttpServletResponse 接口中定义了一个 addCookie 方法,它用于在发送给浏览器的 HTTP 响应消息中增加一个 Set-Cookie 响应头字段。
    • HttpServletRequest 接口中定义了一个 getCookies 方法,它用于从 HTTP 请求消息的 Cookie 请求头字段中读取所有的 Cookie 项。
1-1.Cookie的发送
    • 1 . 创建 Cookie 对象
    • 2. 设置最大时效
    • 3. Cookie 放入到 HTTP 响应报头
     – 如果创建了一个 cookie ,并将他发送到浏览器, 默认情况下它是一个 会话级别 cookie; 存储在浏览器的内存中,用户退出浏览器之后被删除 若希望浏览器将该 cookie 存储在磁盘上,则需要使用 maxAge ,并给出一个以秒为单位的时间。 将最大时效设为 0 则是命令浏览器删除该 cookie
     – 发送 cookie 需要使用 HttpServletResponse addCookie 方法,将 cookie 插入到一个 Set-Cookie HTTP 响应报头中。由于这个方法并不修改任何之前指定的 Set-Cookie 报头,而是创建新的报头,因此将这个方法称为是 addCookie ,而非 setCookie
2.会话cookie和持久cookie的区别
    • 如果 不设置过期时间,则表示这个 cookie 生命周期为浏览器会话期间,只要关闭浏览器窗口, cookie 就消失了。这种生命期为浏览器会话期的 cookie 被称为 会话 cookie 会话 cookie 一般不保存在硬盘上而是保存在内存里
    • 如果设置了过期时间,浏览器就会把 cookie 保存到硬盘上,关闭后再次打开浏览器,这些 cookie 依然有效直到超过设定的过期时间。
    • 存储在硬盘上的 cookie 可以在不同的浏览器进程间共享,比如两个 IE 窗口。而对于保存在内存的 cookie ,不同的浏览器有不同的处理方式。
1-3.应用
    1• 自动登录:不 需要填写用户名和密码等信息,可以自动登录到系统
    2•显示最近浏览的内容

    3• 跟踪用户上次访问站点的时间
    • 功能 帮助网站实现提示客户端计算机上次访问网站的时间
    • 实现原理:
        将每一个会话作为一次访问过程,将每次会话的开始时间作为每次访问网站的时间,然后将这个时间以 Cookie 的形式存储到客户端的计算机中,客户端进行下次访问时通过该 Cookie 回传上次访问站点的时间值。
      为了让 Cookie 信息在客户端浏览器或计算机关闭后仍然保持存在, Cookie 的保存时间被设置为了一年。
Cookie 的 作用范围: 可以作用当前目录和当前目录的子目录. 但不能作用于当前目录的上一级目录。可以通过 setPath 方法来设置 Cookie 的作用范围, 其中 / 代表站点的根目录.


二、Session

    • session ,中文经常翻译为会话,其本来的含义是指有始有终的一系列动作 / 消息,比如打电话是从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个 session
    • session Web 开发环境下的语义又有了新的扩展,它的含义是指 一类用来在客户端与服务器端之间保持状态的解决方案。有时候 Session 也用来指这种解决方案的存储结构。
1.Session机制
    • session 机制采用的是 服务器 端保持 HTTP 状态信息的方案
    • 服务器使用一种类似于散列表的结构 ( 也可能就是使用散列表 ) 来保存信息。
    • 当程序需要为某个客户端的请求创建一个 session 时,服务器首先检查这个 客户端的请求里 是否包含了一个 session 标识 ( sessionId ), 如果已经包含一个 sessionId 则说明以前已经为此客户创建过 session ,服务器就按照 sessionid 把这个 session 检索出来使用 ( 如果检索不到,可能会新建一个,这种情况可能出现在服务端已经删除了该用户对应的 session 对象,但用户人为地在请求的 URL 后面附加上一个 JSESSION 的参数 ) 。如果客户请求不包含 sessionId ,则为此客户创建一个 session 并且生成一个与此 session 相关联的 sessionId 这个 sessionid 将在本次响应中返回给客户端保存

2.保存sessionid的几种方式
    • 保存 sessionid 的方式可以采用 cookie ,这样在交互过程中浏览器可以 自动 的按照规则把这个标识发送给服务器。
    • 由于 cookie 可以被人为的禁用,必须有其它的机制以便在 cookie 被禁用时仍然能够把 sessionid 传递回服务器,经常采用的一种技术叫做 URL 重写 就是把 sessionid 附加在 URL 路径的后面 ,附加的方式也有两种,一种是作为 URL 路径的附加信息,另一种是作为查询字符串附加在 URL 后面。网络在整个交互过程中始终保持状态,就必须在每个客户端可能请求的路径后面都包含这个 sessionid

3.Session  cookie
    • session 通过 SessionID 来区分不同的客户 ,session 是以 cookie URL 重写为基础的, 默认使用 cookie 来实现 系统会创造一个名为 JSESSIONID 的输出 cookie ,这称之为 sessioncookie , 以区别 persistentcookies ( 也就是我们通常所说的 cookie), sessioncookie 是存储于浏览器内存中的,并不是写到硬盘上的 ,通常看不到 JSESSIONID ,但是当把浏览器的 cookie 禁止后, web 服务器会采用 URL 重写的方式传递 Sessionid ,这时地址栏看到
    • session cookie 针对某一次会话而言,会话结束 sessioncookie 也就随着消失了,而 persistentcookie 只是存在于客户端硬盘上的一段文本。
    • 关闭浏览器,只会是浏览器端内存里的 sessioncookie 消失,但不会使保存在服务器端的 session 对象消失,同样也不会使已经保存到硬盘上的持久化 cookie 消失。

4.Session生命周期,即创建与删除
    • 个常见的错误是以为 session 在有客户端访问时就被创建,不一定,1、如果是第一个页面,且JSP的page指定的session被设置为false。2、若当前JSP不是客户端访问的当前WEB应用的第一个资源,且其他页面已经创建一个HttpSession对象,则当前JSP页面会返回一个会话的HttpSession对象,而不会创建一个新的HttpSession对象。然而事实是直到某 server 端程序 ( Servlet ) 调用 HttpServletRequest.getSession (true) 或者 HttpServletRequest.getSession () 这样的语句时才会被创建。
  •session在下列情况下被删除:
    – A .程序调用 HttpSession.invalidate ()
    – B .距离上一次收到客户端发送的 sessionid 时间间隔超过了 session 的最大有效时间
    返回最大时效: getMaxInactiveInterval() 单位是秒
    设置最大时效: setMaxInactiveInterval(int interval)
    可以在 web.xml 文件中配置 Session 的最大时效, 单位是分钟.
    <session-config>
        <session-timeout>30</session-timeout>
    </session-config>
    – C .服务器进程被停止
注意 :关闭浏览器只会使存储在客户端浏览器内存中的 sessioncookie 失效,不会使服务器端的 session 对象失效。

HttpSession的生命周期:

1. 什么时候创建 HttpSession 对象
    1. 是否浏览器访问服务端的任何一个 JSP Servlet ,服务器都会立即创建一个 HttpSession 对象呢?不一定 。若当前的 JSP (或 Servlet )是客户端访问的当前 WEB 应用的第一个资源,且 JSP page 指定的 session 属性 值为 false, 则服务器就不会为 JSP 创建一个 HttpSession 对象;若当前 JSP 不是客户端访问的当前 WEB 应用的第一个资源,且其他页面已经创建一个 HttpSession 对象,则当前 JSP 页面会返回一个会话的 HttpSession 对象,而不会创建一个新的 HttpSession ‘对象
    2. session = “false“  到底表示什么意思?当前 JSP 页面禁用 session 隐含变量!但可以使用其他的显式的 HttpSession 对象
    3. 对于 Serlvet 而言:若 Serlvet 是客户端访问的第一个 WEB 应用的资源,则只有调用了 request.getSession () request.getSession (true) 才会创建 HttpSession 对象

5.两个浏览器窗口访问应用程序会使用同一个session
    • 通常 sessioncookie 是不能跨窗口使用的 (IE 8 版本以前 ) ,当你新开了一个浏览器窗口进入相同页面时,系统会赋予你一个新的 sessionid ,这样信息共享的目的就达不到了。
    • 此时可以先把 sessionid 保存在 persistentcookie ( 通过设置 cookie 的最大有效时间 ) ,然后在新窗口中读出来,就可以得到上一个窗口的 sessionid 了,这样通过 sessioncookie persistentcookie 的结合就可以实现了跨窗口的会话跟踪。

6.Session的超时管理
    • WEB 服务器无法判断当前的客户端浏览器是否还会继续访问,也无法检测客户端浏览器是否关闭,所以,即使客户已经离开或关闭了浏览器, WEB 服务器还要保留与之对应的 HttpSession 对象。
    • 随着时间的推移而不断增加新的访问客户端, WEB 服务器内存中将会因此积累起大量的不再被使用的 HttpSession 对象,并将最终导致服务器内存耗尽。
    • WEB 服务器采用“超时限制”的办法来判断客户端是否还在继续访问 ,如果某个客户端在一定的时间之内没有发出后续请求, WEB 服务器则认为客户端已经停止了活动,结束与该客户端的会话并将与之对应的 HttpSession 对象变成垃圾。
    • 如果客户端浏览器超时后再次发出访问请求, WEB 服务器则认为这是一个新的会话的开始,将为之创建新的 HttpSession 对象和分配新的会话标识号。
    • 会话的超时间隔可以在 web.xml 文件中设置 ,其默认值由 Servlet 容器定义。

  <session-config>

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

  </session-config>

public interface HttpSession {
    String getId(); // 获取session id

    boolean isNew();  //session是否是新的

    void setMaxInactiveInterval(int var1);  //设置session最大时效
    int getMaxInactiveInterval();  // 获取session最大时效

    long getCreationTime();  // 创建时间
    long getLastAccessedTime();  //上次访问时间

    /*最重要的两个方法*/
    Object getAttribute(String var1);  // 获取属性
    void setAttribute(String var1, Object var2);

    Enumeration<String> getAttributeNames();
    void removeAttribute(String var1);

    void invalidate();  //使session无效

    ServletContext getServletContext();


    /** @deprecated */
    HttpSessionContext getSessionContext();

    /** @deprecated */
    Object getValue(String var1);

    /** @deprecated */
    String[] getValueNames();

    /** @deprecated */
    void putValue(String var1, Object var2);

    /** @deprecated */
    void removeValue(String var1);
}
7.利用URL重写实现Session跟踪
    • Servlet 规范中引入了一种补充的会话管理机制, 它允许不支持 Cookie 的浏览器也可以与 WEB 服务器保持连续的会话 。这种补充机制要求在响应消息的实体内容中必须包含下一次请求的超链接,并将会话标识号作为超链接的 URL 地址的一个特殊参数。
    • 将会话标识号以参数形式附加在超链接的 URL 地址后面的技术称为 URL 重写 。如果在浏览器不支持 Cookie 或者关闭了 Cookie 功能的情况下, WEB 服务器还要能够与浏览器实现有状态的会话,就必须对所有可能被客户端访问的请求路径(包括超链接、 form 表单的 action 属性设置和重定向的 URL )进行 URL 重写。
    • HttpServletResponse 接口中定义了两个用于完成 URL 重写方法:
    - encodeURL 方法
    - encodeRedirectURL 方法

8.应用


9. 相对路径和绝对路径:
开发时建议编写“绝对路径”:写绝对路径肯定没有问题,但写相对路径却可能有问题
1). 为什么要解决相对路径的问题: 在有一个 Servlet 转发页面的情况下, 会导致相对路径的混乱.
  a.jsp: <a href="ToBServlet">To B Page2</a>
  ToBServlet: request.getRequestDispatcher("/dir/b.jsp").forward(request, response);
注意, 此时点击 To B Page2 超链接后的浏览器的地址栏的值: http://localhost:8989/day_36/ToBServlet, 实际显示的是dir 路径下的 b.jsp,而 b.jsp 页面有一个超链接: <a href="c.jsp">TO C Page</a>. 默认情况下, c.jsp 应该和 b.jsp 在同一路径下. 此时点击超链接
将在浏览器地址栏显示: http://localhost:8989/day_36/c.jsp. 但在根目录下并没有 c.jsp, 所以会出现路径混乱的问题.
2). 使用绝对路径会解决以上的问题:
绝对路径: 相对于当前 WEB 站点根目录的路径.在当前 WEB 应用的所有的路径前都添加 contextPath 即可.
http://localhost:8989/httpsession/c.jsp:
  - http://localhost:8989/ 是 WEB 站点的根目录,
  - /httpsession_36 是 contextPath,
  - /c.jsp 是相对于当前 WEB 应用的一个文件路径. 我们需要在当前 WEB 应用的任何的路径下都添加上 contextPath, 即可.
比如:
  - <a href="ToBServlet">To B Page2</a> 需改为: <a href="<%= request.getContextPath() %>/ToBServlet">To B Page2</a>
  - response.sendRedirect("a.jsp"); 需改为: response.sendRedirect(request.getContextPath() + "/a.jsp");
  - <form action="AddServlet"></form> 需改为: <form action="<%= request.getContextPath() %>/AddServlet"></form>
3). 在 JavaWEB 应用中 / 代表的是什么:
  ①.当前WEB应用的根路径:http://localhost:8989/httpsession/   若/需要交给Servlet容器来处理
    - 请求转发时:request.getRequestDispatcher("/path/b.jsp").forward(request,response);
    - web.xml文件中映射Servlet访问路径时
        <servlet-mapping>
            <servlet-name>TestServlet</servlet-name>
            <url-pattern>/testServlet</url-pattern>
        </servlet-mapping>
  ②.WEB站点的根路径:http://localhost:8989/    若 / 直接交由浏览器解析
    - 超链接:<a href="/testServlet">To B Page</a>
    - 表达式中的action:<form action="/login.jsp"></form>
    - 做请求重定向的时候:response.sendRedirect("/a.jsp");
总结: / 什么时候代表站点的根目录, 什么时候代表当前 WEB 应用的根目录
    若 / 需要服务器进行内部解析, 则代表的就是 WEB 应用的根目录. 若是交给浏览器了, 则 / 代表的就是站点的根目录
若 / 代表的是 WEB 应用的根目录, 就不需要加上 contextPath 了.

4). 如何获取 contextPath:
  ServletContext: getContextPath()
  HttpServletRequest: getContextPath()

10.避免表单的重复提交
    • 调用 RequestDispatcher.forward () 方法,浏览器所保留的 URL 是先前的表单提交的 URL ,此时点击”刷新” , 浏览器将再次提交用户先前输入的数据,引起重复提交
    • 如果采用 HttpServletResponse.sendRedirct () 方法将客户端重定向到成功页面,将不会出现重复一条问题
利用Session防止表单重复提交
    • 包含 FORM 表单的页面必须通过一个服务器程序动态产生,服务器程序为每次产生的页面中的 FORM 表单都分配一个唯一的随机标识号,并在 FORM 表单的一个隐藏字段中设置这个标识号,同时在当前用户的 Session 域中保存这个标识号。
    • 当用户提交 FORM 表单时,负责接收这一请求的服务器程序比较 FORM 表单隐藏字段中的标识号与存储在当前用户的 Session 域中的标识号是否相同,如果相同则处理表单数据,处理完后清除当前用户的 Session 域中存储的标识号。在下列情况下,服务器程序将忽略提交的表单请求:
     - 当前用户的 Session 中不存在表单标识号
     - 用户提交的表单数据中没有标识号字段
     - 存储在当前用户的 Session 域中的表单标识号与表单数据中的标识号不同
   • 浏览器只有重新向 WEB 服务器请求包含 FORM 表单的页面时,服务器程序才又产生另外一个随机标识号,并将这个标识号保存在 Session 域中和作为新返回的 FORM 表单中的隐藏字段值。
3. 表单的重复提交
1). 重复提交的情况:
  ①. 在表单提交到一个 Servlet, 而 Servlet 又通过 请求转发的方式响应一个 JSP(HTML) 页面,
此时地址栏还保留着 Serlvet 的那个路径, 在响应页面点击 "刷新"
  ②. 在响应页面没有到达时重复点击 "提交按钮".
  ③. 点击 "返回", 再点击 "提交"
2). 不是重复提交的情况: 点击 "返回", "刷新" 原表单页面, 再 "提交"。

3). 如何避免表单的重复提交: 在表单中做一个标记, 提交到 Servlet 时, 检查标记是否存在且是否和预定义的标记一致, 若一致, 则受理请求,
并销毁标记, 若不一致或没有标记, 则直接响应提示信息: "重复提交"
  ①. 仅提供一个隐藏域: <input type="hidden" name="token" value="changwen"/>. 行不通: 没有方法清除固定的请求参数.
  ②. 把标记放在 request 中. 行不通, 因为表单页面刷新后, request 已经被销毁, 再提交表单是一个新的 request.
  ③. 把标记放在 session 中. 可以!
    在原表单页面, 生成一个随机值 token
    在原表单页面, 把 token 值放入 session 属性中
    在原表单页面, 把 token 值放入到 隐藏域 中.

    在目标的 Servlet 中: 获取 session 和 隐藏域 中的 token 值
    比较两个值是否一致: 若一致, 受理请求, 且把 session 域中的 token 属性清除
    若不一致, 则直接响应提示页面: "重复提交"

10.利用Session实现一次性验证码
    • 一次性 验证码的主要目的就是为了限制人们利用工具软件来 暴力 猜测密码,其原理与利用 Session 防止表单重复提交的原理基本一样,只是将表单标识号变成了验证码的形式,并且要求用户将提示的验证码手工填写进一个表单字段中,而不是通过表单的隐藏字段自动回传给服务器。
    • 服务器程序接收到表单数据后,首先判断用户是否填写了正确的验证码,只有该验证码与服务器端保存的验证码匹配时,服务器程序才开始正常的表单处理流程。
    • 密码猜测工具要逐一尝试每个密码的前题条件是先输入正确的验证码,而验证码是一次性有效的,这样基本上就阻断了密码猜测工具的自动地处理过程
使用 HttpSession 实现验证码
1). 基本原理: 和表单重复提交一致:
    在原表单页面, 生成一个验证码的图片, 生成图片的同时, 需要把该图片中的字符串放入到 session 中.
    在原表单页面, 定义一个文本域, 用于输入验证码.

    在目标的 Servlet 中: 获取 session 和 表单域 中的 验证码的 值
    比较两个值是否一致: 若一致, 受理请求, 且把 session 域中的 验证码 属性清除
    若不一致, 则直接通过重定向的方式返回原表单页面, 并提示用户 "验证码错误"




  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值