JavaWeb 09 会话和状态管理

1、HTTP协议的特点

⑴ 纯文本
⑵ 无状态
无状态是指服务器不能区分多个请求是否来自同一个用户,但是有时我们需要让服务器知道多个请求是来自同一个用户。例如:网上购物

这时就需要用到Cookie和Session

2、Cookie

概念

Cookie是服务器发送给浏览器的,用于区分不同用户的一段信息

运行机制

⑴ 浏览器第一次发送请求给服务器时,服务器会创建Cookie对象
服务器将Cookie对象发送给浏览器
⑶ 以后浏览器再次发送请求给服务器时,浏览器就会携带该Cookie对象
⑷ 服务器就会根据不同的Cookie对象,来区分不同的用户

相关方法

创建Cookie对象


public Cookie(String name, String value) {}
第一个参数:Cookie对象的名字;第二个参数:Cookie对象的值

将Cookie对象发送给浏览器
public void addCookie(Cookie cookie);
用HttpServletResponse来调用,需要传递一个Cookie对象

注意事项:
⑴ Cookie对象的名字不能使用中文
⑵ Cookie对象的值可以使用中文,但是需要使用字符集进行编码,所以不建议使用中文
⑶ 创建Cookie对象后,一定要用HttpServletResponse来调用addCookie方法,否则浏览器是无法保存该Cookie对象的,因为没有给浏览器,就等同于白创建Cookie对象

示例:

    Cookie cookie = new Cookie("username", "admin");
    response.addCookie(cookie);

创建一个Cookie对象,并发送给浏览器

获取Cookie对象

public Cookie[] getCookies();
用HttpServletResponse来调用,获取保存所有Cookie对象的数组

获取Cookie对象的值
⑴ 获取Cookie对象的名字

    public String getName() {
      return name;
    }

通过Cookie对象来调用,获取其名字

   ⑵ 获取Cookie对象的值
    public String getValue() {
      return value;
    }

通过Cookie对象来调用,获取其值

示例:

    Cookie[] cookies = request.getCookies();

    // 判读是否有Cookie
    if (null != cookies) {
        int len = cookies.length;

        for (int i = 0; i < len; i++) {
            Cookie cookie = cookies[i];

            System.out.println("名字:" + cookie.getName() + ",值:" + cookie.getValue());
        }

    }

获取所有的Cookie对象的名字和值

修改Cookie对象

⑴ 方式一:
创建一个同名的Cookie对象,这样会将之前的Cookie的值覆盖掉

⑵ 方式二:
调用getCookies 方法,遍历所有的Cookie对象,找出指定名字的Cookie对象,并调用setValue方法
public void setValue(String newValue) {
value = newValue;
}

注意:调用setValue 方法后,一定要调用HttpServletResponse的addCookie 方法,将修改后的Cookie发送给浏览器,否则等同于没有修改

示例:
【修改名叫”username”的Cookie对象的值】

方式一:

Cookie cookie = new Cookie("username", "newAdmin");
response.addCookie(cookie);

方式二:

  Cookie[] cookies = request.getCookies();

    // 判读是否有Cookie
    if (null != cookies) {
        int len = cookies.length;

        for (int i = 0; i < len; i++) {
            Cookie cookie = cookies[i];

            // 查找是否有指定名字的Cookie对象
            if ("username".equals(cookie.getName())) {
                cookie.setValue("newAdmin");
                // 发送修改后的Cookie对象给浏览器
                response.addCookie(cookie);
            }

        }

    }

持久化Cookie对象

Cookie对象默认的是会话级别的
会话级别即浏览器一旦关闭,Cookie对象就会失效

通过调用setMaxAge 方法来设置Cookie对象的有效时间
public void setMaxAge(int expiry) {
maxAge = expiry;
}
需要传入有效时间,单位是秒

maxAge > 0 时:Cookie对象在maxAge秒后失效
maxAge = 0时:Cookie对象立即失效
maxAge < 0时,默认【int maxAge = -1;】会话级别的Cookie

注意:该方法需要在调用addCookie 方法之前,被调用,否则无效

示例:

    Cookie cookie = new Cookie("timeCookie1", "60sCookie");
    cookie.setMaxAge(60);
    response.addCookie(cookie);

创建一个有效时间为60秒的Cookie

    Cookie cookie = new Cookie("timeCookie2", "defaultCookie");
    cookie.setMaxAge(0);
    response.addCookie(cookie);

创建一个立即失效的Cookie

    Cookie cookie = new Cookie("timeCookie3", "defaultCookie");
    // cookie.setMaxAge(-1);
    response.addCookie(cookie);

创建一个默认【会话级别】的Cookie

设置Cookie的有效路径

Cookie对象的有效路径,即访问哪些地址时,浏览器会携带对应的Cookie对象
默认的有效路径是当前WEB应用的根路径,可以通过setPath 方法来指定Cookie对象的有效路径
public void setPath(String uri) {
path = uri;
}
需要传入一个地址。注意该地址由浏览器解析

示例:

    Cookie cookie = new Cookie("pathCookie1", "defaultPathCookie");
    response.addCookie(cookie);

创建一个有效路径为当前WEB应用的根目录的Cookie

    Cookie cookie = new Cookie("pathCookie2", "helloPathCookie");
    // 该路径由浏览器解析
    cookie.setPath(request.getContextPath() + "/hello");
    response.addCookie(cookie);

创建一个有效路径为 当前WEB应用根目录/hello 的Cookie

Cookie的用途

⑴ 广告推荐
⑵ 免登陆【设置Cookie的有效时间】

Cookie的不足

⑴ Cookie的键和值都是明文的,不安全
⑵ 不同的浏览器对Cookie的大小和数量有限制
⑶ 携带过多的Cookie对象会费流量
⑷ Cookie对象中的值只能保存字符串(String)

3、Session

概念

Session即HttpSession,其用来保存用户的信息

运行机制

⑴ 服务器创建Session对象,该对象有一个全球唯一的ID
在创建Session对象的同时,会创建一个特殊的Cookie,该Cookie对象的名字是一个固定值:JSESSIONID。这个Cookie对象的value值,就是Session对象所拥有的全球唯一的ID。服务器会将这个Cookie发送给浏览器
⑶ 以后浏览器再次请求服务器时,就会携带这个特殊的Cookie对象
⑷ 服务器获取这个特殊的Cookie对象的value值后,就会在服务器中寻找与之对应的Session对象,并以此来区分不同的用户

相关方法

创建Session对象


⑴ 如果访问的是JSP页面
一旦访问JSP页面,就会创建Session对象。这个Session对象是在JSP文件对应的 .java 文件中的 _jspService 方法中创建的,通过调用 pageContext.getSession();
JSP页面的Session对象的创建

⑵ 如果访问的是HTML页面
这时只有访问Servlet时,通过调用HttpServletRequest的getSession方法时,才会创建Session对象
public HttpSession getSession();

Tips:可以通过调用HttpSession的getId 方法,来获取保存在JSESSIONID这个特殊的Cookie对象的value的值
public String getId();

注意:在一次会话中,只能使用一个Session对象。Session对象一旦被创建,则在整个会话中,无论在哪获取到的,都是同一个Session对象

通过持久化特殊的Cookie对象JSESSIONID,来达到找到Session中保存的用户的目的

    由于Cookie对象默认是会话级别的,所以一旦浏览器被关闭,该Cookie对象就会失效
    所以,一旦浏览器被关闭,特殊的Cookie对象:JSESSIONID,就会失效,同时就无法找到与之对应的Session对象,也就找不到Session中保存的用户
    对此我们可以通过持久化JSESSIONID,这个特殊的Cookie对象,来找到之前保存到Session中的用户的目的

示例:

    // 获取所有的Cookie对象
    Cookie[] cookies = request.getCookies();

    if (null != cookies) {
        int len = cookies.length;

        // 遍历
        for (int i = 0; i < len; i++) {
            Cookie cookie = cookies[i];
            String cookieName = cookie.getName();
            // 匹配名字
            if ("JSESSIONID".equals(cookieName)) {
                // 设置有效时间
                cookie.setMaxAge(60);
                // 发送给浏览器
                response.addCookie(cookie);
            }
        }

    }

设置Session对象的最大空闲时间

Session对象的最大空闲时间默认是30分钟,在tomcat服务器的总的配置文件web.xml中,有设置
Session的默认的最大空闲时间

我们也可以手动地设置Session对象的最大空闲时间。需要调动setMaxInactiveInterval 方法
public void setMaxInactiveInterval(int interval);
需要传入设置Session的最大空闲时间的参数,单位是秒

interval > 0:Session对象在interval秒后失效
interval = 0:Session对象立即失效
interval < 0:Session对象永不失效

注意:传入0时,在tomcat7.0中不好用。推荐使用invalidate方法,该方法是让指定的Session对象立即失效
public void invalidate();

示例:

    HttpSession session = request.getSession();
    session.setMaxInactiveInterval(3);

设置Session对象的最大空闲时间为3秒

    HttpSession session = request.getSession();
      /*
       * tomcat7.0不好使
       * 
       * session.setMaxInactiveInterval(0);
       */
    session.invalidate();

设置Session对象立即失效

      HttpSession session = request.getSession();
      session.setMaxInactiveInterval(-1);

设置Session对象永不失效

4、钝化和活化

钝化

Session和Session域中保存的对象一起从内存中被序列化到硬盘上的过程
服务器关闭的时候,会发生钝化的现象
会在:

 工作空间\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\work\Catalina\localhost\项目名

下面生成一个:SESSIONS.ser 文件
钝化所产生的文件

活化

Session和Session域中保存的对象一起从硬盘中被反序列化到内存中的过程
服务器再次被开启的时候,会发生活化的现象

注意事项

⑴ 要保证正常的钝化和活化,Session域中保存的对象,所对应的类要实现Serializable【序列化】接口。如果其内部有其他类型,则对应的类也需要实现Serializable接口,否则整个对象都不能被反序列化
⑵ 如果服务器不是正常地被关闭。例如点击了Console窗口的Terminate按钮,这时就无法实现活化,因为钝化并没有实现【服务器不是正常地被关闭的】

5、URL重写

为啥要重写URL

当浏览器禁用Cookie时,每次向服务器发起请求的时候,就不会携带Cookie对象,也就无法获取对应的Session对象

解决办法

⑴ 可以调用HttpServletResponse对象的encodeURL方法
public String encodeURL(String url);
将要跳转的url传入,如果浏览器禁用了Cookie,则会在URL地址的后面携带JSESSIONID。如果浏览器没有禁用Cookie,则URL地址后面不会携带JSESSIONID

URL地址;jsessionid=???

⑵ 可以调用HttpServletResponse对象的encodeRedirectURL方法
public String encodeRedirectURL(String url);
该方法和encodeURL类似

⑶ 可以使用JSTL的

其中value属性就是要跳转的页面的URL地址。该标签返回一个URL地址

示例

<a href="<%=response.encodeRedirectURL("pages/show.jsp")%>">方式一</a><br /><br />
<a href="<%=response.encodeRedirectURL("pages/show.jsp")%>">方式二</a><br /><br />
<a href="<c:url value="pages/show.jsp"></c:url>">方式三</a><br /><br />

6、 表单的重复提交

概念

同一个表单、同样的数据,向服务器提交多次

危害:
⑴ 给服务器造成了不必要的压力
⑵ 可能会造成数据库中保存了很多的垃圾数据

重复提交的三种情况

转发的情况下,表单提交成功后,多次刷新成功页面


⑴ 产生的原因
转发的情况,因为浏览器的地址栏中的地址并没有改变,所以多次刷新,都相当于重复地向action所指向的资源文件处提交请求

⑵ 解决方案
改用重定向

使用示例

【登录JSP页面】

<h1>转发,成功后多次刷新成功界面</h1>
<form action="<%=request.getContextPath()%>/loginServlet" method="post">
    用户名:<input type="text" name="username" />
    <input type="submit" value="登录" />
</form>

【LoginServlet】

protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    System.out.println("接收用户请求");

    // request.getRequestDispatcher("/success.jsp").forward(request, response);
    // 改为重定向
    response.sendRedirect(request.getContextPath() + "/success.jsp");
}

在网速很慢的情况下,多次单击表单的提交按钮

⑴ 产生的原因
提交按钮可以被单击多次

⑵ 解决方案
让提交按钮只能单击一次

使用示例

【登录JSP页面】

<script type="text/javascript" src="<%=request.getContextPath()%>/js/jquery-1.7.2.min.js"></script>
<script type="text/javascript">
    $(function(){
        $(":submit").click(function(){
            // 将提交按钮禁用
            $(this).attr("disabled", true);
            // 提交表单
            $("form").submit();
        });
    });
</script>
</head>
<body>
    <h1>网速慢,多次单击提交按钮</h1>
    <form action="<%=request.getContextPath()%>/loginServlet" method="post">
        用户名:<input type="text" name="username" />
        <input type="submit" value="登录" />
    </form>
</body>

【LoginServlet】

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

    // 模拟网速慢
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println("接收用户请求");

    response.sendRedirect(request.getContextPath() + "/success.jsp");
}

表单提交成功后,单击浏览器的后退按钮,在不刷新页面的情况下,再次单击提交按钮

⑴ 产生的原因
服务器可以处理重复提交的表单

⑵ 解决方案
使用token(记号、令牌)

实现的步骤:
① 在服务器中生成一个全球唯一的字符串作为token,并将其保存到Session域中

全球唯一的字符串,可以使用java.util.UUID类的方法来创建。UUID是一个32位的字母、数字混排的由机器码和时间戳计算得到的一个全球唯一的字符串

     public static UUID randomUUID() {}
     获取一个UUID。这是一个静态方法,可以使用UUID来调用
Tips:可以调用UUID的toString方法,将其转换为字符串

② 并将token放到表单的隐藏域中,随着表单一起提交到服务器
③ 在服务器获取Session域中的token,并从隐藏域中获取token,两个对比
    ⒈ 如果相等,就是正常提交
    ⒉ 如果不等,就是重复提交
正常的提交处理完后,一定要将Session域中的token移除

使用示例

【登录JSP页面】

<%
    // 创建一个UUID字符串
    String uuid = UUID.randomUUID().toString();
    // 放到Session域中
    session.setAttribute("sessionUUID", uuid);
%>

<h1>提交成功后,单击浏览器的返回按钮,不刷新页面,再次单击提交按钮</h1>
<form action="<%=request.getContextPath()%>/loginServlet" method="post">
    <input type="hidden" name="requestUUID" value="<%=uuid%>" />
    用户名:<input type="text" name="username" />
    <input type="submit" value="登录" />
</form>

【LoginServlet】

protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    // 获取隐藏域中的UUID
    String requestUUID = request.getParameter("requestUUID");

    // 获取Session对象
    HttpSession session = request.getSession();
    // 获取Session域中的UUID
    String sessionUUID = (String) session.getAttribute("sessionUUID");

    if (null != sessionUUID && requestUUID.equals(sessionUUID)) {
        // 正常提交

        // 一定要移除Session中保存的UUID!
        session.removeAttribute("sessionUUID");

        System.out.println("接收用户请求");
        response.sendRedirect(request.getContextPath() + "/success.jsp");
    } else {
        // 重复提交,则重定向到其他页面
        response.sendRedirect(request.getContextPath() + "/failure.jsp");
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值