【Web第十五天】会话技术cookie+session

会话技术

1.什么是会话?

  为了实现某一个功能, 浏览器和服务器之间可能会产生多次的请求和响应, 从浏览器访问服务器开始, 到最后访问服务器结束为止, 这期间产生的多次请求和响应加在一起, 就可以称之为浏览器和服务器之间的一次会话.

 

  在一次会话中很可能会产生一些数据, 那么如何来保存会话中产生的数据??

           ~request对象作用范围太小了

           ~servletContext对象作用范围太大了

 

1.Cookie

2.1.Cookie概述

    Cookie是将会话中产生的数据保存在客户端, 所以Cookie是一个客户端技术

 

    Cookie保存数据的原理:

      浏览器向服务器发送请求, 请求中包含着需要保存的数据, 服务器获取数据, 通过Set-Cookie响应头将数据再响应给浏览器, 让浏览器自己保存.

      浏览器再次访问服务器时, 会在请求中, 通过一个Cookie请求头带着上次保存的数据, 服务器可以通过Cookie请求头来获取数据. 通过这种形式来保存会话中产生的数据!

 

   由于Cookie技术是将会话产生的数据保存在客户端, 客户端各自保存各自的数据, 需要时再次带给服务器, 因此不会发生混乱!

2.2.实现Cookie

  Cookie是基于 Set-Cookie响应头和Cookie请求头来保存会话中产生的数据

 

    response.setHeader(“Set-Cookie”, “prod=小米手机”);

    request.getHeader(“Cookie”) //prod=小米手机

 

  SUN公司为了简化Cookie的操作, 提供了一套Cookie的API:

             Cookie cookie = new Cookie(String name,String value);

             cookie.getName(); //获取Cookie的名字

             cookie.getValue(); //获取Cookie的value值

             cookie.setValue(); //设置或修改cookie的value值

2.3.添加Cookieresponse响应中

    response.addCookie(cookie);//将Cookie信息添加到response响应中, 并发送给浏览器

    该方法可以调用多次, 即可以将多个Cookie添加到response响应.

2.4.获取Cookie

    Cookie[] cs = request.getCookies();//返回请求中所有的Cookie组成的数组

               需要注意的是: 如果请求中没有cookie, 该方法返回值是null。

2.5.设置Cookie存活时间

    setMaxAge();//该方法可以设置Cookie的最大生存时间。

    如果不设置该方法, Cookie默认是会话级别的Cookie, Cookie是保存在浏览器的内存中, 如果浏览器关闭, 随着浏览器内存的释放, Cookie信息也会丢失.

    如果设置了setMaxAge方法, 并且设置了一个有效的时间, Cookie将不会再保存到浏览器的内存中, 而是以文件的形式保存在浏览器的临时文件夹中(即本地硬盘上).

2.6.设置Cookie路径

setPath();//设置Cookie路径, 如果不设置该方法, 默认浏览器会在访问

           http://localhost/day12/servlet/                                          会带着Cookie

           http://localhost/day12/servlet/xxxx                                会带着Cookie

           http://localhost/day12/servlet/xxxx/xxx                        会带着Cookie

           http://localhost/day12/                                                              不会带着Cookie

 

也就说, 浏览器会在访问 http://localhost/day12/servlet/ 或者其子孙路径时都会带着Cookie, 该路径是由发送Cookie的Servlet(CookieDemo2)所在的路径决定!(即http://localhost/day12/servlet/CookieDemo2所在路径为 http://localhost/day12/servlet/

 

如果希望浏览器在访问当前应用(/day12)下任何一个资源时都能够带着Cookie, 我们应该把 http://localhost/day12/servlet/ 改成 http://localhost/day12/ 这个路径。

 

设置Cookie的路径:

//setPath(“http://localhost/day12/”);

//setPath(“/day12/”);

setPath(request.getContextPath()+“/”)

 

2.7.设置domain(了解)

    setDomain();//设置浏览器在访问哪一个主机时将会带着Cookie, 如果不设置, 浏览器在访问发送Cookie的主机时将会带着Cookie

 

    例如:

      time  localhost主机  cookie.setDomain(“http://www.baidu.com”)

      //浏览器可以检测到并非baidu发的cookie,所以会拒绝接收。

      time  localhost主机  cookie.setDomain(“localhost”);

      //默认就是自己,设置多此一举,而且若设置此方法,有的浏览器看到此方法也会拒绝接收

 

    如果在创建Cookie之后, 设置了setDomain方法, 浏览器可能会拒绝接收Cookie, 所以最好不要设置!!!

 

2.8.删除Cookie

    API中没有提供直接删除Cookie的方法!!

    如果想删除Cookie, 可以向浏览器再次发送个 同名、同path、同domain的Cookie给浏览器, 并设置Cookie最大生存时间为0. 由于浏览器是根据Cookie的名字/path和domian来区分一个Cookie, 后发的Cookie会覆盖之前的Cookie, 又因为后发的Cookie最大生存为0, 浏览器收到之后也会删除后发的cookie, 通过这种方式可以删除一个Cookie!!

2.9.EasyMall登陆功能之记住用户名

1.点击登录,

 

 

 

7.登录成功跳转到首页,用重定向

8.未找到,提示错误,回到登录页面,请求转发

 

 

代码如下:

在WebRoot下login.jsp中,修改<form>标签属性action:

<form action="<%= request.getContextPath() %>/servlet/LoginServlet" method="POST">

 

在web包中创建servlet:LoginServlet,并添加如下代码:

  //1.处理请求乱码

    request.setCharacterEncoding("utf-8");

    //2.获取请求参数

    String username = request.getParameter("username");

    String password = request.getParameter("password");

    String remname = request.getParameter("remname");

    //3.查询用户名和密码是否正确

    Connection conn = null;

    PreparedStatement ps = null;

    ResultSet rs = null;

    try {

        conn = JDBCUtils.getConnection();

        String sql = "select * from user where username=? and password=?";

        ps = conn.prepareStatement(sql);

        ps.setString(1, username);

        ps.setString(2, password);

        rs = ps.executeQuery();

        if (rs.next()) {//用户名密码正确

      //4.当用户名密码正确时,实现记住用户名功能

             if("true".equals(remname)){

                 /*

                  * 由于username要通过cookie头发送给浏览器,所以遵循http协议,

                  * 但是浏览器不支持中文,会将数据进行url编码,例如:张飞-> %E5%BC%A0%E9%A3%9E

                  * 所以给浏览器发送数据时,需要进行url编码

                  * URLEncoder.encode(str,"utf-8");将中文字符进行编码

                  * URLDecoder.decode(str,"utf-8");将中文字符进行解码

                  */

                 Cookie cookie = new Cookie("remname", URLEncoder.encode(username,"utf-8"));

                 cookie.setPath(request.getContextPath()+"/");

                 cookie.setMaxAge(60*60*24*30);//设置30天

                 response.addCookie(cookie);

             }else{      //取消记住用户名(删除cookie)

                 Cookie cookie = new Cookie("remname","");

                 cookie.setPath(request.getContextPath()+"/");

                 cookie.setMaxAge(0);//设置30天

                 response.addCookie(cookie);

             }

          //5.去登陆(在session中保存用户的个人信息作为登录的标识)

             //TODO

            

          //6.登录成功后,跳转回主页(重定向)

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

        }else{        //用户名或密码不正确

             //跳转回登录页面,并提示用户用户名或者密码错误

             request.setAttribute("msg", "用户名或者密码错误");

             request.getRequestDispatcher("/login.jsp").forward(request, response);

             return;

        }

    } catch (Exception e) {

        e.printStackTrace();

        throw new RuntimeException();

    }finally{

        JDBCUtils.close(conn, ps, rs);

    }

 

在WebRoot下login.jsp中,获取提示消息:在<table>中最前面加入如下代码:

<table>

    <tr>

        <td colspan="2" style="color:red;text-align: center">

             <%= request.getAttribute("msg") ==  null ? "" : request.getAttribute("msg")%>

        </td>

    </tr>

 

同样,在login.jsp中,如果cookie中保存了用户名,需要回显用户名,在用户名对应的<tr>中加入如下代码:

<tr>

        <% //jsp片段,可以写多行java代码。

             //1.获取请求中的所有Cookie信息

             Cookie[] cookies = request.getCookies();

             Cookie remCookie = null;

             if(cookies != null){

                 for(Cookie cookie : cookies){

                     if("remname".equals(cookie.getName())){

                         remCookie = cookie;

                     }

                 }

             }

             String username = "";

             if(remCookie != null){

                 username = remCookie.getValue();

                 //对用户名进行url解码(注意需要导包)

                 username = URLDecoder.decode(username,"utf-8");

             }

         %>

        <td class="tdx">用户名:</td>

        <td><input type="text" name="username" value="<%= username %>"/></td>

</tr>

 

      最后,在login.jsp中,如果记住用户名之前是勾选的,说明Cookie中有值,以此可以判断记住用户名之前是否勾上,来进行回显。在记住用户名对应的<tr>中加入如下代码:

<tr>

        <td colspan="2">

             <input type="checkbox" name="remname" value="true"

             <%= remCookie == null ? "" : "checked = 'checked'" %>

             />记住用户名

             <input type="checkbox" name="autologin" value="true"/>30天内自动登陆

        </td>

</tr>

 

 

3.Session

3.1.Session概述

    Session是将会话中产生的数据保存在了服务器端, 是服务器端的技术。

3.2.Session的原理

  浏览器向服务器发送请求, 将需要保存的数据带给服务器, 服务器通过请求获取数据, 接着去服务器内部检查有没有为当前浏览器服务的session, 如果有直接拿过来使用, 如果没有就创建一个session对象为当前浏览器服务. 将数据保存在session内部.

当浏览器再次访问服务器时, 服务器可以找到为当前浏览器服务的session, 从中取出数据, 通过这种方式也可以保存会话中产生的数据!

 

由于session技术是将会话中产生的数据保存在服务器内部的session中, 每一个浏览器都会有各自的session, 因此也不会发生混乱!

 

3.3.session是一个域对象

  域对象: 如果一个对象具有可以被看见的范围, 利用对象上的map可以在整个范围内实现数据的共享!

  session中提供了如下方法来操作域中的属性:

    setAttribute(String name, Object value);  

    getAttribute(String name);

    removeAttribute(String name);

    getAttributeNames();

 

  session域对象的特征:

  生命周期:

           创建: 第一次调用request.getSession()方法时创建session对象。

 

      如果服务器中有session,就直接拿过来用,如果没有,则创建一个session拿过来用。

          HttpSession session1 = request.getSession();

          或

          HttpSession session2 = request.getSession(true);

 

      如果服务器中有session,就直接拿过来用,如果没有,并不会创建,而是返回null。

          HttpSession session3 = request.getSession(false);

 

销毁:

    超时死亡: 如果session 30分钟不使用就会超时销毁! 可以通过 web.xml中文件修改session的超时时间.

      例如:在web.xml中的<web-app>标签中加入如下代码,其中时间设置的单位是分钟。

<session-config>

    <session-timeout>1</session-timeout>

  </session-config>

 

自杀: 当调用session.invalidate()方法时会立即销毁session.

 

意外身亡: 当服务器意外关闭时(停电/宕机), session会立即销毁

  当服务器正常关闭时, session并不会销毁, 而是以文件的形式保存到tomcat服务器中的work目录下, 这个过程叫做session的钝化. 当服务器再次开启时, 钝化的session还会恢复回来, 这个过程叫做session的活化。

 

作用范围:整个会话

 

主要功能:在整个会话范围内实现数据的共享!

 

 

3.4. Easymall登陆功能的实现

代码如下:

因为有很多数据要放入到session中,所以可以创建一个User对象,将user对象保存到session中。

创建包cn.tedu.bean,创建类User,加入如下代码:(同时加入无参和有参构造,加入get和set方法)

private int id;

    private String username;

    private String password;

    private String nickname;

    private String email;

 

在LoginServlet中的第5步,去登陆中加入如下代码:

  //5.去登陆(在session中保存用户的个人信息作为登录的标识)

        User user = new User();

        user.setId(rs.getInt("id"));

        user.setUsername(rs.getString("username"));

        user.setPassword(rs.getString("password"));

        user.setNickname(rs.getString("nickname"));

        user.setEmail(rs.getString("email"));

                

        //将user对象保存到session中

        request.getSession().setAttribute("user", user);

 

如果登陆,页面中应该显示已经登录。在_head.jsp中的登录注册部分代码改造为:

<div id="content">

    <!-- 如果用户未登录,则提示用户登录或注册 -->

    <% if(request.getSession().getAttribute("user") == null){ %>

        <a href="<%= request.getContextPath() %>/login.jsp">登录</a> &nbsp;&nbsp;|&nbsp;&nbsp;

        <a href="<%= request.getContextPath() %>/regist.jsp">注册</a>

    <!-- 如果用户已登录,则提示欢迎xxx回来 -->

    <% }else{ %>

        欢迎 <%= ((User)request.getSession().getAttribute("user")).getUsername() %> 回来,

        <a href="<%= request.getContextPath() %>/servlet/LogoutServlet">退出</a>

    <% } %>

</div>

 

最后,需要实现退出功能。在web包中创建servlet:LogoutServlet,并添加如下代码:

  //立即杀死session

    request.getSession().invalidate();

    //退出之后跳转回首页

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

 

3.5.Session案例二:实现购物车(略)

测试后发现问题:session保存时间是30分钟,但是关闭浏览器后,session也消失了。

3.6.在关闭浏览器之后, 如何获取关闭之前的session

  Session是基于一个名为JSESSIONID 的Cookie进行工作的

  浏览器第一次访问服务器, 需要保存数据, 服务器会创建一个session对象为浏览器服务, 并且服务器会为session分配一个独一无二的编号, 就是session的id, 服务器在响应时会将session的id通过Cookie的形式发送给浏览器保存, 浏览器会将Cookie保存在内存中(即保存sessionid的Cookie是一个会话级别的Cookie), 这个Cookie会随着浏览器的关闭而销毁, 所以在浏览器关闭之前可以访问session, 关闭之后, 由于浏览器将session的id弄丢了, 所以服务器没办法根据id找到对应的Session。

 

解决方案:

           我们可以向浏览器再发送一个名称为 JSESSIONID的Cookie, 值为session的id(session.getId()获取sessionid), 并且path为web应用的根路径, 并设置Cookie的最大生存时间为一个有效的时间, 这时服务器在响应时, 会发送两个保存sessionid的Cookie, 其中一个Cookie会保存在浏览器的临时文件夹中, 即使多次关闭浏览器, Cookie也不会销毁, session的id也不会销毁,服务器依然可以根据id找到对应的session

3.7.Easymall验证码的校验

代码如下

在EasyMall项目下,ValiImageServlet中最后添加如下代码:

  //将图片保存到response缓冲区中,再响应给浏览器

        vc.drawImage(response.getOutputStream());

       

        //获取图片上的验证码

        String code = vc.getCode();

        //将验证码文本保存到session中,用于后期的校验

        request.getSession().setAttribute("code", code);

 

在RegistServlet中的验证码是否正确验证中加入如下代码:

  //>>验证码是否正确

        //从session中获取验证码内容

        String code = (String) request.getSession().getAttribute("code");

        if (!valistr.equalsIgnoreCase(code)) {

             //将提示存入request域中,通过转发将消息带到regist.jsp中

             request.setseAttribute("msg", "验证码不正确!");

             request.getRequestDispatcher("/regist.jsp").forward(request, response);

             return;

        }

 

 

posted @ 2018-03-29 16:31  songyao  阅读(135)  评论(0编辑  收藏  举报