首页 > 代码库 > Cookie学习笔记(一)

Cookie学习笔记(一)

Cookie 简介

 

一、简介

     说起来,Cookie应该是一种应用较久的技术了。早在HTML刚刚出现的时候,在每个独立的页面之间没有办法记录和 标识不同的用户。后来人们就发明了Cookie技术,当用户访问网页时,它能够在访问者的机器上创立一个文件,我们把它叫作Cookie,写一段内容进去,来标识不同的用户。如果下次用户再访问这个网页的时候,它又能够读出这个文件里面的内容,这样网页就知道上次这个用户已经访问过该网页了。


      虽然现在网页的制作技术比起几年以前已经发展了许多。不过有些时候,Cookie还是能够帮我们很多忙的。


      Cookie 是Web服务器发送给客户端的一小段信息,客户端发送请求时,服务器处理请求对客户端做出响应。
如果服务器需要在客户端记录某些数据时,就会向客户端发送一Cookie,客户端接收并保存该Cookie,而且客户
端每次访问该服务器上的页面时就会将Cookie 随请求数据一同发送给服务器。

 

 

Cookie的使用

 

一、向客户程序发送cookie

1创建cookie对象

    Cookie c = new Cookie(“userId”,”landril”);

2设置最大时效

    默认该cookie是存储在浏览器的内在中,用户关闭浏览器则被删除,下面的方法是将cookie存储在硬盘上。     c.setMaxAge(60*60*24);    //一天,如果设置为0则是删除该cookie

3、向客户端添加Cookie

    将cookie放入到HTTP响应报头,可以使用HttpServletResponse的addCookie方法,此方法不修改之前指定    的Set-Cookie头信息,而是创建新的头信息。
    response.addCookie(c);
    注意:设置cookie的步骤为创建cookie对象,设置最大时效,将cookie放入响应头信息,即发送到客户程序。


二、从客户端读取cookie

 

1获取Cookie数组

    调用HttpServletRequest的getCookies得到一个Cookie对象的数组

 

2对数组进行循环,调用cookiegetName方法,获取具体的cookie的值

 

    Cookie[] cookies = request.getCookies();
        if(cookies != null){
            for(int i=0;i < cookies.length;i++){
             Cookie c = cookies[i];
             if(“userId”.equals(c.getName())){
                 System.out.println(c.getValue());
              }
            }
    }


使用Cookie记录访问数

 

1.用户第一次访问网页时的操作

     ...
    out.println("你是第1 次登录该网页。在此之前没有相关Cookie信息。");   //打印登录信息
    cookie=new Cookie("accpCount", "1");      //Cookie对象,并设置名称与值
    cookie.setMaxAge(60*60*24);      //设置最长保留时间为1天
    response.addCookie(cookie);      //添加到响应头信息,返回到客户端
    ...

2.用户非第一次访问网页时的操作

      ...
    Cookie[] cookies = request.getCookies();     //从request对象中获取客户端Cookie信息

    int count = 0;         //初始访问次数为0
    Cookie cookie=null;     //定义Cookie对象

    if (cookies != null) {     //判断request对象中是否存在Cookie信息
         for (int i = 0; i < cookies.length; i++) {     //使用for循环遍历Cookie数组
             cookie = cookies[i];     //获取数组元素

             if ("accpCount".equals(cookie.getName())) {     //判断Cookie的名称是否为"accpCount"
                 count = Integer.parseInt(cookie.getValue())+1;     //如果相等,加1
                 cookie.setValue(count+"");      //重新设置Cookie对象的value值
                cookie.setMaxAge(60*60*24);     //设置保留最长时间
                response.addCookie(cookie);     //返回到客户端
                 out.println("你是第" + count + "次登录该网页。");
              
                break;
              }
          }
    }
    ...

3.完整的使用Cookie对象获取客户Cookie数据,并通过Cookie名称取出相应的值,在页面上显示登录次数

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

         response.setContentType("text/html;charset=gb2312");
         PrintWriter out = response.getWriter();
         out .println("");
         out.println("");
         out.println(" ");
         out.println(" ");

         Cookie[] cookies = request.getCookies();

         int count = 0;
         boolean bool=true;
         Cookie cookie=null;

         if (cookies != null) {
             for (int i = 0; i < cookies.length; i++) {
                 cookie = cookies[i];

                 if ("accpCount".equals(cookie.getName())) {
                     count = Integer.parseInt(cookie.getValue())+1;
                     cookie.setValue(count+"");
                     cookie.setMaxAge(60*60*24);
                     response.addCookie(cookie);
                     out.println("你是第" + count + "次登录该网页。");

                    bool=false;
                    break;
                  }
              }
             }
             if(bool){
                 out.println("你是第1 次登录该网页。在此之前没有相关Cookie信息。");
                 cookie=new Cookie("accpCount", "1");
                 cookie.setMaxAge(60*60*24);
                 response.addCookie(cookie);
             }

             out.println(" ");
             out.println("");
             out.flush();
             out.close();
        }
        ...

4.运行效果图

 

 

使用Cookie记录用户名和密码

 

一、用户登录操作

     在用户填写登录数据后,提交表单进行登录验证,如果用户名和密码正确,则把用户名和密码保存在客户端。页面如图1所示。

 
图1 保存Cookie信息的登录页面

         实现代码如下所示。
         ...
     response.setContentType("text/html;charset=gbk");
         //获取用户名
         String userName = request.getParameter("username");
         //获取用户密码
         String password = request.getParameter("password");

         //验证用户是否合法
         if ("accp".equals(userName) && "accp".equals(password)) {
                   //如果合法,把用户名和密码保存在客户端Cookie中

                    //获取客户选择的Cookie保存时间
                    String cookieDate = request.getParameter("cookieDate");
                   int date = 0;

                   if (cookieDate != null&& cookieDate != "")
                             date = Integer.parseInt(cookieDate);

                   //创建用户名Cookie对象
                   Cookie cookieName = new Cookie("accpLoginName", userName);
                    //设置Cookie保存时间
                  cookieName.setMaxAge(date);
                   //添加到客户端
                   response.addCookie(cookieName);

                   //创建用户密码Cookie对象
                   Cookie cookiePass = new Cookie("accpLoginPass", password);
                   //设置保存Cookie时间
                   cookiePass.setMaxAge(date);
                    //添加到客户端
                  response.addCookie(cookiePass);

                    response.sendRedirect("success.jsp");
         } else {
                    response.sendRedirect("fail.jsp");
         }
         ...


二、用户再次登录时操作

     服务器首先读取客户端Cookie信息,如果存在用户名和密码数据,则直接登录,否则显示登录页面。
         实现代码如下所示。

        response.setContentType("text/html;charset=gbk");
         //用户名和密码是否存在以及合法的判断参数
         boolean bool=false;
         //获取客户端Cookie数组
        Cookie[] cookies = request.getCookies();

         //判断客户端是否存在Cookie对象
        if (cookies != null) {
                  //预定义保存用户名和密码的变量

                   String userName = "";
                   String password = "";
                  //遍历Cookie数组

                for (int i = 0; i < cookies.length; i++) {
                            //取得每一个Cookie对象
                           Cookie cookie = cookies[i];
                          //判断Cookie的名称是否等于"accpLoginName"
                          if ("accpLoginName".equals(cookie.getName())) {
                                  userName = cookie.getValue();
                           }                  

                          //判断Cookie的名称是否等于"accpLoginPass"
                         if ("accpLoginPass".equals(cookie.getName())) {
                                   password = cookie.getValue();
                          }
                   }
                 //判断用户名和密码是否合法
                 if ("accp".equals(userName) && "accp".equals(password))
                          bool=true;
                  }

                //判断对客户端的Cookie的操作是否成功,成功则显示登录成功后的页面,
                 //否则,重定向到登录页面
                 if(bool)
                         response.sendRedirect("success.jsp");
                 else
                          response.sendRedirect("login.jsp");

 

附Cookie会话简介

HTTP是一种无状态技术,这意味着每个单独的HTML页面都是一个无关的实体。当人们穿过站点时,HTTP没有用于跟踪用户或保持变量的方法。尽管浏览器会跟踪你访问过的页面,但是服务器不会记录谁看到过什么内容。如果服务器不能够跟踪用户,就不可能有购物车或自定义个性化Web站点的存在。使用服务器端技术(如PHP),可以克服Web的无状态性。可用于此目的的两种最佳的PHP工具是cookie和会话。

你可能已经知道,cookie在用户的Web浏览器中存储数据。在用户访问产生cookie的站点上的页面时,服务器可以从该cookie读取数据。会话把数据存储在服务器本身上面。会话一般比cookie更安全,并且可以存储多得多的信息。这两种技术都很容易与PHP一起使用,并且值得了解。

在本章中,你将看到cookie和会话的使用。演示该信息的示例将是一个登录系统,它基于现有的users数据库。

11.1  建立登录页面

登录过程涉及几个组成部分:

l    用于提交登录信息的表单;

l    确认必要信息已提交的验证例程;

l    比较提交的信息与存储的信息的数据库查询;

l    用于存储反映成功登录的数据的cookie或会话。

后续页面然后将包含一些检查,用于确认用户已登录(用于限制对该页面的访问)。当然,还有一个注销过程,它涉及清除表示登录状态的cookie或会话数据。

首先,让我们获取其中一些常见的元素并把它们存放在单独的文件中。然后,需要这种功能的页面可以包含必要的文件。像这样分解逻辑将使得接下来的一些脚本更容易阅读和编写,并有助于减少它们之间的冗余性。我设计了两个可包含的文件。第一个文件将包含登录页面的大部分内容,包括:标题、错误报告、表单和脚注(参见图11-1)。

建立登录页面

(1) 在文本编辑器或IDE中创建一个新的PHP页面(参见脚本11-1)。

脚本11-1  login_page.inc.php脚本创建完整的登录页面(包括表单)并报告错误。需要显示登录页面的其他页面将包含它

1 <?php # Script 11.1 - login_page.inc.php

2

3 // This page prints any errors associated with logging in

4 // and it creates the entire login page, including the form.

5

6 // Include the header:

7 $page_title = ‘Login‘;

8 include (‘includes/header.html‘);

9

10  // Print any error messages, if they exist:

11  if (!empty($errors)) {

12    echo ‘<h1>Error!</h1>

13  <p class="error">The following error(s) occurred:<br />‘;

14    foreach ($errors as $msg) {

15    echo " - $msg<br />\n";

16    }

17    echo ‘</p><p>Please try again.</p>‘;

18  }

19 

20  // Display the form:

21  ?>

22  <h1>Login</h1>

23  <form action="login.php" method="post">

24    <p>Email Address: <input type="text" name="email" size="20" maxlength="80" /> </p>

25    <p>Password: <input type="password" name="pass" size="20" maxlength="20" /></p>

26    <p><input type="submit" name="submit" value="http://www.mamicode.com/Login" /></p>

27  <input type="hidden" name="submitted" value="http://www.mamicode.com/TRUE" />

28  </form>

29 

30  <?php // Include the footer:

31  include (‘includes/footer.html‘);

32  ?>

(2) 包括标题。

$page_title = ‘Login‘;

include (‘includes/header.html‘);

本章将利用最初在第3章创建然后在第8章修改过的相同模板系统。

(3) 如果有任何出错消息,就打印它们。

if (!empty($errors)) {

  echo ‘<h1>Error!</h1>

  <p class="error">The following error(s) occurred:<br />‘;

  foreach ($errors as $msg) {

    echo " - $msg<br />\n";

  }

  echo ‘</p><p>Please try again.</p>‘;

}

这段代码也是在第8章中开发的。如果有任何错误存在(在$errors数组变量中),就会把它们打印成一份无序列表(参见图11-2)。

 

图11-2  带有错误报告的登录表单和页面

(4) 显示表单。

?>

<h1>Login</h1>

<form action= "login.php" method= "post">

  <p>Email Address: <input type= "text" name="email" size= "20" maxlength="80" /> </p>

  <p>Password: <input type= "password" name= "pass" size= "20" maxlength="20" /></p>

  <p><input type="submit" name= "submit" value= "http://www.mamicode.com/Login" /></p>

  <input type= "hidden" name= "submitted" value= "http://www.mamicode.com/TRUE" />

</form>

HTML表单只需要两个文本输入框:一个用于电子邮件地址,另一个用于密码。输入框的名称与sitename数据库(登录系统就基于该数据库)的users表中的那些列名称匹配。

为了更容易创建HTML表单,首先关闭PHP部分。该表单不是黏性表单,但是可以轻松地添加代码来实现这一点(但是仅仅对于电子邮件地址可以如此,密码则不能是黏性的)。

(5) 完成页面。

<?php

include (‘includes/footer.html‘);

?>

(6) 将文件另存为login_page.inc.php,将其存放在Web目录中(该目录与第8章中的文件header.html、footer.html和style.css都在includes文件夹中)。

该页面将使用.inc.php扩展名,指示它是一个可包含的文件并且它包含PHP代码。

ü提示

l    这个脚本包含includes目录内的标题和脚注文件似乎不合乎逻辑,因为该脚本也在相同的目录内。这段代码会工作,因为该脚本将被主目录内的页面包含;因此包含引用是关于父目录的,而不是关于这个目录的。

 

11.2  创建登录函数

除了存储在login_page.inc.php中的登录页面外,还有一些功能在本章中的多个脚本中也很常见。在下一个脚本中(登录/注销系统中的其他页面也会包含该脚本),将定义两个函数。

许多页面最终将把用户从一个页面重定向到另一个页面。例如,在成功登录后,将把用户带到loggedin.php。如果用户访问loggedin.php,但他们没有登录,就应该把他们带到index.php。重定向使用第10章中介绍的header()函数。用于重定向的语法如下:

header (‘Location: http://www.example.com/page.php‘);

由于这个函数将把浏览器发送到page.php,应该在这之后立即使用exit()终止当前脚本:

header (‘Location: http://www.example.com/page.php‘);

exit();

如果不这样做,当前脚本将会继续运行(只是不在Web浏览器中运行而已)。

header()调用中的位置值应该是绝对URL(www.example.com/page.php,而不仅仅是page.php)。你可以硬编码这个值,不过更好的做法是动态确定它。下一个脚本中的第一个函数就用于执行该任务。

本章中的多个脚本将使用的另外一些代码用于验证登录表单。这是一个包含三个步骤的过程:

(1) 确认提供了电子邮件地址;

(2) 确认提供了密码;

(3) 确认提供的电子邮件地址和密码与数据库中存储的电子邮件地址和密码匹配(在注册过程中)。

因此,下一个脚本将定义两个不同的函数。在接下来的步骤中将解释每个函数是如何工作的。

创建登录函数

(1) 在文本编辑器或IDE中创建一个新的PHP文档(参见脚本11-2)。

脚本11-2  login_functions.inc.php脚本中定义了两个函数,将被登录/注销过程中的不同脚本使用

1 <?php # Script 11.2 - login_functions.inc.php

2

3 // This page defines two functions used by the login/logout process.

4

5 /* This function determines and returns an absolute URL.

6 * It takes one argument: the page that concludes the URL.

7 * The argument defaults to index.php.

8 */

9 function absolute_url ($page = ‘index.php‘) {

10 

11    // Start defining the URL...

12    // URL is http:// plus the host name plus the current directory:

13    $url = ‘http://‘ . $_SERVER[‘HTTP_HOST‘] . dirname($_SERVER[‘PHP_SELF‘]);

14 

15    // Remove any trailing slashes:

16    $url = rtrim($url, ‘/\\‘);

17 

18    // Add the page:

19    $url .= ‘/‘ . $page;

20 

21    // Return the URL:

22    return $url;

23 

24  } // End of absolute_url() function.

25 

26 

27  /* This function validates the form data (the email address and password).

28  * If both are present, the database is queried.

29  * The function requires a database connection.

30  * The function returns an array of information, including:

31  * - a TRUE/FALSE variable indicating success

32  * - an array of either errors or the database result

33  */

34  function check_login($dbc, $email = ‘‘, $pass = ‘‘) {

35 

36    $errors = array(); // Initialize error array.

37 

38    // Validate the email address:

39    if (empty($email)) {

40    $errors[] = ‘You forgot to enter your email address.‘;

41    } else {

42      $e = mysqli_real_escape_string($dbc, trim($email));

43    }

44 

45    // Validate the password:

46    if (empty($pass)) {

47      $errors[] = ‘You forgot to enter your password.‘;

48    } else {

49      $p = mysqli_real_escape_string($dbc, trim($pass));

50    }

51 

52    if (empty($errors)) { // If everything‘s OK.

53 

54      // Retrieve the user_id and first_name for that email/password combination:

55      $q = "SELECT user_id, first_name FROM users WHERE email=‘$e‘ AND pass=SHA1(‘$p‘)";

56      $r = @mysqli_query ($dbc, $q); // Run the query.

57 

58      // Check the result:

59      if (mysqli_num_rows($r) == 1) {

60 

61      // Fetch the record:

62      $row = mysqli_fetch_array ($r, MYSQLI_ASSOC);

63 

64      // Return true and the record:

65      return array(true, $row);

66 

67      } else { // Not a match!

68      $errors[] = ‘The email address and password entered do not match those on file.‘;

69      }

70 

71    } // End of empty($errors) IF.

72 

73    // Return false and the errors:

74    return array(false, $errors);

75 

76  } // End of check_login() function.

77 

78  ?>

因为这个文件将由其他文件包含,所以它不必包含任何HTML。

(2) 开始定义新函数。

function absolute_url ($page = ‘index.php‘) {

absolute_url()函数将返回一个绝对URL,它对于运行这些脚本的站点是正确的。动态执行该操作的好处是(与仅仅硬编码http://www.example.com/page.php相对):可以在一个服务器(比如你自己的计算机)上开发代码,然后把它移到另一个服务器上,而无需更改这段代码。

该函数带有一个可选的参数:最终的目标页面名称。默认值是index.php。

(3) 开始定义URL。

$url = ‘http://‘ . $_SERVER [‘HTTP_HOST‘] . dirname($_SERVER[‘PHP_SELF‘]);

首先,把http://以及主机名称(它可以是localhost或www.example.com)赋予$url。这里使用dirname()函数添加了当前目录的名称,以免在子文件夹内发生重定向。$_SERVER[‘PHP_SELF‘]引用当前脚本(它将是调用这个函数的脚本),包括目录名称。整个值可能是/somedir/page.php。dirname()函数将只从那个值中返回目录部分(即/somedir/)。