javajavawebcookiesession
14尚硅谷_Cookie&Session王振国 - 课堂笔记.pdf

一、会话技术

会话:一次会话中包含多次请求和响应
一次会话表示浏览器第一次给服务器发送请求,会话建立,直到有一方断开为止
功能:在一次会话的多次请求间共享数据
方式

  • 客户端会话技术:Cookie
  • 服务器端会话技术:Session

    二、Cookie

    Cookie 是服务器通知客户端保存键值对的一种技术,客户端有了 Cookie 之后,每次请求都会发送给服务器
    浏览器对单个 Cookie 有大小限制(4KB),对同一个域名下的总cookie数量也有限制(20个)
    作用

  • Cookie一般用于存储少量的安全性较低的数据

  • 在没有登陆的情况下,完成服务器对客户端的身份识别,如没有登录百度账号的前提下打开百 度,设置搜索引擎搜索时不提示,以后打开浏览器访问百度时,不会再出现搜索提示框,原理:百度服务器将设置的Cookie信息保存到浏览器,下次访问百度时,百度服务器获取浏览器的Cookie,根据Cookie的值决定要不要显示提示框

    使用步骤

  1. 创建Cookie对象,参数中绑定数据(键值对)

**new Cookie(String name, String value)**

  1. 客户端向服务器发送请求后,服务器向客户端发送Cookie对象
    **response.addCookie(Cookie cookie)**
  2. 客户端收到Cookie后,再次发送请求时,服务器获取从客户端发来的Cookie对象
    **Cookie[] request.getCookies()**
  3. 服务器得到Cookie对象后,使用**getName()****getValue()**方法得到Cookie对象的数据

    创建cookie

    一次可以发送多个Cookie对象,使用response调用多次addCookie方法即可

    1. protected void createCookie(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    2. //1 创建Cookie对象
    3. Cookie cookie1 = new Cookie("key1", "value1");
    4. Cookie cookie2 = new Cookie("key2", "value2");
    5. //2 通知客户端保存Cookie
    6. response.addCookie(cookie1);
    7. response.addCookie(cookie2);
    8. response.getWriter().write("Cookie 创建成功");
    9. // 方案二:
    10. // 1、先查找到需要修改的Cookie对象
    11. // 2、调用setValue()方法赋于新的Cookie值。
    12. // 3、调用response.addCookie()通知客户端保存修改
    13. }

    获取cookie

    1. protected void getCookie(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    2. Cookie[] cookies = request.getCookies();
    3. for (Cookie cookie : cookies) {
    4. // getName方法返回Cookie的key(名)
    5. // getValue方法返回Cookie的value值
    6. response.getWriter().write("cookie[" + cookie.getName() + "=" + cookie.getValue() + "] <br/>");
    7. }
    8. Cookie iWantCookie = CookieUtils.findCookie("key1", cookies);
    9. // 如果不等于null,说明赋过值,也就是找到了需要的Cookie
    10. if (iWantCookie != null) {
    11. response.getWriter().write("找到了需要的Cookie");
    12. }
    13. }

    查找指定名称的Cookie对象工具类

    1. public class CookieUtils {
    2. /**
    3. * 查找指定名称的Cookie对象
    4. *
    5. * @param name
    6. * @param cookies
    7. * @return
    8. */
    9. public static Cookie findCookie(String name, Cookie[] cookies) {
    10. if (name == null || cookies == null || cookies.length == 0) {
    11. return null;
    12. }
    13. for (Cookie cookie : cookies) {
    14. if (name.equals(cookie.getName())) {
    15. return cookie;
    16. }
    17. }
    18. return null;
    19. }
    20. }

    修改cookie的值

    cookie的value值不能包含符号 及 中文(tomcat8以后可以中文),除非使用BASE64编码
    **public void setValue(String newValue)** 设置Cookie的值

    1. protected void updateCookie(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    2. /*
    3. * 无论以下哪种方案,cookie的value值都不能包含符号 及 中文,除非使用BASE64编码
    4. */
    5. // 方案一:
    6. // 1、先创建一个要修改的同名的Cookie对象,同时赋于新的Cookie值。
    7. // Cookie cookie = new Cookie("key1", "newValue1");
    8. // 2、调用response.addCookie( Cookie ); 通知 客户端 保存修改
    9. // resp.addCookie(cookie);
    10. // resp.getWriter().write("key1的Cookie已经修改好");
    11. // 方案二:
    12. // 1、先查找到需要修改的Cookie对象
    13. Cookie cookie = CookieUtils.findCookie("key2", req.getCookies());
    14. if (cookie != null) {
    15. // 2、调用setValue()方法赋于新的Cookie值。
    16. cookie.setValue("newValue2");
    17. // 3、调用response.addCookie()通知客户端保存修改
    18. resp.addCookie(cookie);
    19. resp.getWriter().write("key2的Cookie已经修改好");
    20. }
    21. }

    浏览器查看cookie

    image.png

    设置cookie生命周期

    Cookie在浏览器中保存的时间

  • 默认情况下,当浏览器关闭后,Cookie数据被销毁
  • 持久化存储,使用Cookie对象的 **setMaxAge(int seconds)** 方法
  • 正数:将Cookie数据写到硬盘中存储,参数指定存活的秒数,时间到后,数据失效, 此时间指的是创建cookie后开始计时,并不是关闭浏览器后才开始计时。
  • 负数:默认情况
  • 零:删除cookie信息 ```java protected void defaultLife(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie cookie = new Cookie(“defaultLife”, “defaultLife”); cookie.setMaxAge(-1); resp.addCookie(cookie); }

protected void deleteNow(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 先找到你要删除的Cookie对象 Cookie cookie = CookieUtils.findCookie(“key1”, req.getCookies());

  1. if (cookie != null) {
  2. // 调用setMaxAge(0); 表示马上删除,都不需要等待浏览器关闭
  3. cookie.setMaxAge(0);
  4. resp.addCookie(cookie);
  5. resp.getWriter().write("key1的Cookie已经被删除");
  6. }

}

protected void life3600(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie cookie = new Cookie(“life3600”, “life3600”); cookie.setMaxAge(60 * 60); resp.addCookie(cookie); // 设置Cookie一小时之后被删除。无效 resp.getWriter().write(“已经创建了一个存活一小时的Cookie”); }

<a name="bTf5r"></a>
###  设置Cookie 的有效路径 Path
Cookie的共享问题:

- 一个Tomcat服务器中,部署了多个web项目,这些web项目cookie的共享说明

默认情况cookie无法共享<br />使用Cookie对象的`**setPath(String path)**`方法设置cookie的获取范围

- 默认情况,参数是web工程路径,只有这个工程才可以访问到,其余工程无法访问
- 如果要共享,可以设置参数为”/” ( /被浏览器解析得到的地址为http://ip:port/ )
- 不同的Tomcat服务器间cookie的共享说明

使用Cookie对象的setDomain(String path)方法,参数设置为一级域名,则一级域名相同的不同服务器之间Cookie可共享<br />如:setDomain(“.baidu.com”),则tieba.baidu.com与news.baidu.com等的cookie可共享
```java
protected void testPath(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    Cookie cookie = new Cookie("path1", "path1");
    // getContextPath() ===>>>>  得到工程路径
    cookie.setPath(req.getContextPath() + "/abc");
    resp.addCookie(cookie);
    resp.getWriter().write("创建了一个带有Path路径的Cookie");
}

Cookie 练习—-免输入用户名登录

image.png

public class LoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        String password = req.getParameter("password");

        if ("cess".equals(username) && "123123".equals(password)) {
            Cookie cookie = new Cookie("username", username);
            cookie.setMaxAge(60 * 60 * 24 * 7);
            resp.addCookie(cookie);
            System.out.println("登录 成功");
        } else {
            System.out.println("登录 失败");
        }
    }
}

三、Session

  1. Session 就一个接口(HttpSession)
  2. Session 就是会话。它是用来维护一个客户端和服务器之间关联的一种技术
  3. 每个客户端都有自己的一个 Session 会话
  4. Session 会话中,我们经常用来保存用户登录之后的信息
  5. Session用于存储一次会话的多次请求数据,存在服务器端,一次会话只有一个session对象

Session可以存储任意类型,任意大小的数据
Session与Cookie的区别

  • Session存储数据在服务器端,Cookie在客户端
  • Session没有数据大小的限制,Cookie有(4KB)
  • Session数据安全,Cookie相对不安全

    创建 Session 和获取(id 号,是否为新)

    创建和获取 Session 的 API 是一样的
    **HttpSession request.getSession()**
    第一次调用是:创建 Session 会话
    之后调用都是:获取前面创建好的 Session 会话对象
    **isNew()**判断到底是不是刚创建出来的(新的,true 表示刚创建,false 表示获取之前创建
    **getId()**得到 Session 的会话 id 值。每个会话都有一个身份证号,就是 ID 值,且是唯一的

    protected void createOrGetSession(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      // 创建和获取Session会话对象
      HttpSession session = request.getSession();
    
      // 判断 当前Session会话,是否是新创建出来的
      boolean isNew = session.isNew();
    
      // 获取Session会话的唯一标识 id
      String id = session.getId();
      response.getWriter().write("得到的session,它的id是:" + id + "<br/>");
      response.getWriter().write("这个Session是否是新创建的:" + isNew + " <br /> ");
    }
    

    Session 域数据的存取

    **void setAttribute(String name, Object value)**
    **Object getAttribute(String name)**
    **void removeAttribute(String name)** ```java protected void setAttribute(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getSession().setAttribute(“key1”, “value1”); response.getWriter().write(“已经往Session中保存了数据”); }

protected void getAttribute(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Object attribute = request.getSession().getAttribute(“key1”); response.getWriter().write(“从Session中获取出key1的数据是:” + attribute); }

<a name="SWeRm"></a>
###  Session 生命周期控制
Session被销毁的方式

- 服务器关闭
- Session对象调用`**invalidate()**`
- Session默认失效时间:30分钟,可以到web.xml中修改配置文件修改默认失效时间
```xml
 <session-config>
     <session-timeout>30</session-timeout>
 </session-config>

**getMaxInactiveInterval()**
**setMaxInactiveInterval(int seconds)**

/*
 * 如果希望你的web工程,默认的Session的超时时长为其他时长。
 * <session-config>
 *      <session-timeout>20</session-timeout>
 * </session-config>
 * 你可以在你自己的 web.xml 配置文件中做以上相同的配置,就可以修改你的 web 工程所有 Seession 的默认超时时长。
 */
protected void defaultLife(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // 获取了Session的默认超时时长
    int maxInactiveInterval = request.getSession().getMaxInactiveInterval();
    response.getWriter().write("Session的默认超时时长为:" + maxInactiveInterval + " 秒");
}

protected void life3(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // 设置当前Session3秒后超时
    request.getSession().setMaxInactiveInterval(3);
    response.getWriter().write("当前Session已经设置为3秒后超时");
}

protected void deleteNow(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // 让Session会话马上超时
    request.getSession().invalidate();
    response.getWriter().write("Session已经设置为超时(无效)");
}

服务器不关闭客户端关闭之后重新登录,两次获取的Session是否为同一个?

  • 通常不是,Cookie消失,无法找到原先的session,此时request.getSession()获取的为新的session
  • 如果需要相同,设置cookie最大存活时间为正数,在此期间cookie被浏览器持久化保存,再次打开浏览器,cookie依然存在

image.png
客户端不关闭,服务器关闭之后,两次获取的Session是否为同一个?
不是同一个Session,但是为了保证数据的不丢失,Tomcat服务器自动完成:

  • Session的钝化:

在服务器正常关闭之前,将Session对象序列化到硬盘上

  • Session的活化:

在服务器启动之后,将Session文件反序列化成为内存中的Session对象
注意:也就是说即使获取的不是同一个Session,但是Session中的数据都是相同的

浏览器和 Session 之间关联的技术内幕

image.png

public void login(HttpServletRequest request, HttpServletResponse response) 
    throws ServletException, IOException {
    // 1、获取请求的参数
    String username = request.getParameter("username");
    String password = request.getParameter("password");

    // 调用 userService.login()登录处理业务
    User loginUser = userService.login(new User(null, username, password, null));

    // 如果等于null,说明登录失败!
    if (loginUser == null) {
        // 把错误信息,和回显的表单项信息,保存到Request域中
        request.setAttribute("msg", "用户名或密码错误");
        request.setAttribute("username", username);

        // 跳回登录页面
        request.getRequestDispatcher("/pages/user/login.jsp").forward(request, response);
    } else {
        // 登录成功
        // 保存用户登录的信息到Session域中
        request.getSession().setAttribute("user", loginUser);

        // 跳到成功页面login_success.html
        request.getRequestDispatcher("/pages/user/login_success.jsp")
            .forward(request, response);
    }
}

四、表单重复提交之——-验证码

表单重复提交有三种常见的情况

  1. 提交完表单,服务器使用请求转来进行页面跳转。这个时候,用户按下功能键 F5,就会发起最后一次的请求,造成表单重复提交问题。解决方法:使用重定向来进行跳转
  2. 用户正常提交服务器,但是由于网络延迟等原因,迟迟未收到服务器的响应,此时,用户以为提交失败,着急然后多点了几次提交操作,也会造成表单重复提交
  3. 用户正常提交服务器,服务器也没有延迟,但是提交完成后,用户回退浏览器重新提交,也会造成表单重复 提交

image.png

谷歌 kaptcha 图片验证码的使用

谷歌验证码 kaptcha 使用步骤如下

  1. 导入谷歌验证码的 jar 包——kaptcha-2.3.2.jar
  2. 在 web.xml 中去配置用于生成验证码的 Servlet 程序

    <servlet>
         <servlet-name>KaptchaServlet</servlet-name>
         <servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
    </servlet>
    <servlet-mapping>
     <servlet-name>KaptchaServlet</servlet-name>
     <url-pattern>/kaptcha.jpg</url-pattern>
    </servlet-mapping>
    
  3. 在表单中使用 img 标签去显示验证码图片并使用它

    <form action="http://localhost:8080/10_cookie_session/registerServlet" method="get">
     用户名:<input type="text" name="username"> <br/>
     验证码:<input type="text" name="code" style="width: 70px">
     <img src="http://localhost:8080/10_cookie_session/kaptcha.jpg" alt="" style="width: 100px;height: 20px"> <br/>
     <input type="submit" value="注册">
    </form>
    
  4. 在服务器获取谷歌生成的验证码和客户端发送过来的验证码比较使用

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     // 获取Session中的验证码
     Object token = req.getSession().getAttribute(KAPTCHA_SESSION_KEY);
     // 删除 Session中的验证码
     req.getSession().removeAttribute(KAPTCHA_SESSION_KEY);
    
     // 获取用户名
     String username = req.getParameter("username");
     // 获取验证码
     String code = req.getParameter("code");
    
     if (token != null && token.equals(code)) {
    
         System.out.println("保存到数据库 " + username);
         resp.sendRedirect(req.getContextPath() + "/ok.jsp");
     } else {
         System.out.println("请不要重复提交表单");
     }
    }
    

    某些浏览器只能刷新一次验证码的原因

    image.png
    为验证码标签添加单击事件,点击可以更新验证码
    但有些浏览器会缓存验证码图片,造成无法刷新,单击事件src后添加一个能够区分的参数就可以如 new Date() ```html 9 cookie & session - 图7

$(“#code_img”).click(function () { // 在事件响应的function函数中有一个this对象。这个this对象,是当前正在响应事件的dom对象 // src属性表示验证码img标签的 图片路径。它可读,可写 // alert(this.src); this.src = “${basePath}kaptcha.jpg?d=” + new Date(); }); ```