JavaWeb——Cookie入门

1.会话跟踪

什么是会话呢?从用户进入一个网站浏览开始,到关闭浏览器为止称为一次会话。

会话包含以下意思:

  1. 会话是一段时间,这段时间默认从打开浏览器开始到关闭浏览器为止。
  2. 一次会话可以包含多次请求与响应。

而Web应用程序是使用HTTP协议传输数据的。HTTP协议是无状态的协议。一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接。这就意味着服务器无法从连接上跟踪会话。即用户A购买了一件商品放入购物车内,当再次购买商品时服务器已经无法判断该购买行为是属于用户A的会话还是用户B的会话了。要跟踪该会话,必须引入一种机制。

Cookie就是这样的一种机制。它可以弥补HTTP协议无状态的不足。在Session出现之前,基本上所有的网站都采用Cookie来跟踪会话。

2.Cookie

Cookie意为“甜饼”,是由W3C组织提出,最早由Netscape社区发展的一种机制。目前Cookie已经成为标准,所有的主流浏览器如IE、Netscape、Firefox、Opera等都支持Cookie。

由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份。怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理

Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。
在这里插入图片描述

查看某个网站颁发的Cookie很简单。在浏览器地址栏输入**javascript:alert (document. cookie)**就可以了(需要有网才能查看)。JavaScript脚本会弹出一个对话框显示本网站颁发的所有Cookie的内容。
如果浏览器不支持Cookie(如大部分手机中的浏览器)或者把Cookie禁用了,Cookie功能就会失效。

不同的浏览器采用不同的方式保存Cookie。

3.Cookie常用的方法

Java中把Cookie封装成了javax.servlet.http.Cookie类。每个Cookie都是该Cookie类的对象。服务器通过操作Cookie类对象对客户端Cookie进行操作。通过request.getCookies()获取客户端提交的所有Cookie(以Cookie[]数组形式返回),通过response.addCookie(Cookie cookie)向客户端设置Cookie。

Cookie对象使用key-value属性对的形式保存用户状态,一个Cookie对象保存一个属性对,一个request或者response同时使用多个Cookie。因为Cookie类位于包javax.servlet.http.*下面,所以JSP中不需要import该类。

Cookie在Java中是用javax.servlet.http.Cookie类表示的。Cookie不是JSP的内置对象,Cookie常用的方法如下:
在这里插入图片描述

4.Cookie在java中如何使用

任务1:显示上次访问时间

创建动态web项目,命名为cookie,在cookie中创建write.jsp用于写入Cookie,创建read.jsp用于读取cookie,实现记录用户上次访问时间的业务。

write.jsp代码

<%
//创建Cookie对象
Cookie cookieUserName =new Cookie("username","songjinxiang");
//设置存活时间
cookieUserName.setMaxAge(60*60*10);
//添加cookie
response.addCookie(cookieUserName);
//创建Cookie对象,中文需要编码
Cookie cookieName =new Cookie("name",java.net.URLEncoder.encode("林冲", "utf-8"));
//设置存活时间
cookieName.setMaxAge(60*60);
//添加cookie
response.addCookie(cookieName);
//创建cookie对象
Cookie cookieTime =new Cookie("time",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
//添加cookie
response.addCookie(cookieTime);
%>

Cookie为什么要设置最大存活时间呢????

如果没有为Cookie指定存活时间,则Cookie保存在客户端的内存中,当浏览器关闭Cookie即消亡。如果指定了存活时间,则Cookie保存在客户端的文件中,即使浏览器关闭,在存活时间内Cookie内容依然存在。

read.jsp代码

<%
Cookie[] cookies = request.getCookies();
if(cookies != null){
    for(int i = 0;i < cookies.length;i++){
	     Cookie cookie = cookies[i];
	     if("name".equals(cookie.getName())){	
		      out.print(java.net.URLDecoder.decode(cookie.getValue(),"utf-8"));
		      out.print("你好,你上次访问时间是");
	     }
	     if("time".equals(cookie.getName())){	
		      out.print(cookie.getValue());
		      out.print("<br />");
	     }
    }
}
%>

代码解析

  1. request对象的getCookies()方法用于从请求报头中获取当前站点向客户端写入过的所有有效Cookie,返回Cookie数组。
  2. 通过迭代数组可以找到需要的Cookie对象,getName()获取Cookie的键,getValue()获取Cookie的值。
  3. URLDecoder.decode()用于对Cookie中存储的汉字解码。

5.Unicode编码:保存中文

Cookie中使用Unicode字符时需要对Unicode字符进行编码,否则会乱码。

//创建Cookie对象,中文需要编码
Cookie cookieName =new Cookie("myname",java.net.URLEncoder.encode("林冲", "utf-8"));
response.addCookie(cookieName);

//============================================================
//取值需要解码
 Cookie[] cookies = request.getCookies();
  for (Cookie c:cookies) {
    if(c.getName().equals("myname"))
        out.println(c.getName()+":"+java.net.URLDecoder.decode(c.getValue(),"UTF-8"));
  }

提示:Cookie中保存中文只能编码。一般使用UTF-8编码即可。不推荐使用GBK等中文编码,因为浏览器不一定支持,而且JavaScript也不支持GBK编码。

6.Cookie的有效期

Cookie的maxAge决定着Cookie的有效期,单位为秒(Second)。Cookie中通过getMaxAge()方法与setMaxAge(int maxAge)方法来读写maxAge属性。

如果maxAge属性为正数,则表示该Cookie会在maxAge秒之后自动失效。浏览器会将maxAge为正数的Cookie持久化,即写到对应的Cookie文件中。无论客户关闭了浏览器还是电脑,只要还在maxAge秒之前,登录网站时该Cookie仍然有效。

下面代码中的Cookie信息将永远有效。

// 新建Cookie
Cookie cookie = new Cookie("username","songjinxiang");
// 设置生命周期为MAX_VALUE
cookie.setMaxAge(Integer.MAX_VALUE);  
// 输出到客户端
response.addCookie(cookie);   

下面代码的Cookie信息将保存10天

// 新建Cookie
Cookie cookie = new Cookie("username","songjinxiang");
// 设置生命周期为10天
cookie.setMaxAge(60*60*24*10);  
// 输出到客户端
response.addCookie(cookie); 

如果maxAge为负数,则表示该Cookie仅在本浏览器窗口以及本窗口打开的子窗口内有效,关闭窗口后该Cookie即失效。maxAge为负数的Cookie,为临时性Cookie,不会被持久化,不会被写到Cookie文件中。Cookie信息保存在浏览器内存中,因此关闭浏览器该Cookie就消失了。Cookie默认的maxAge值为–1。

如果为负数,该Cookie为临时Cookie,关闭浏览器即失效,浏览器也不会以任何形式保存该Cookie。如果为0,表示删除该Cookie

Cookie机制没有提供删除Cookie的方法,因此通过设置该Cookie即时失效实现删除Cookie的效果。失效的Cookie会被浏览器从Cookie文件或者内存中删除。

// 新建Cookie
Cookie cookie = new Cookie("username","songjinxiang");
//如果设置为负值的话,则为浏览器进程Cookie(内存中保存),关闭浏览器就失效;
cookie.setMaxAge(-1);  
//如果设置为0,则立即删除该Cookie。
cookie.setMaxAge(0); 
// 输出到客户端
response.addCookie(cookie); 

7.Cookie的修改、删除

Cookie并不提供修改、删除操作。如果要修改某个Cookie,只需要新建一个同名的Cookie,添加到response中覆盖原来的Cookie。

如果要删除某个Cookie,只需要新建一个同名的Cookie,并将maxAge设置为0,并添加到response中覆盖原来的Cookie。注意是0而不是负数。负数代表其他的意义。读者可以通过上例的程序进行验证,设置不同的属性。

// 新建同名Cookie的值,覆盖原有的cookie,达到修改的效果
Cookie cookie = new Cookie("username","litong");
//也可设置不同的失效时间20天
cookie.setMaxAge(60*60*24*20);
// 输出到客户端
response.addCookie(cookie); 
// 新建Cookie
 Cookie cookie = new Cookie("username","songjinxiang");
 //如果设置为0,则立即删除该Cookie。
 cookie.setMaxAge(0); 
 // 输出到客户端
 response.addCookie(cookie); 

注意:修改、删除Cookie时,新建的Cookie除value、maxAge之外的所有属性,例如name、path、domain等,都要与原Cookie完全一样。否则,浏览器将视为两个不同的Cookie不予覆盖,导致修改、删除失败。

8.Cookie的域名

Cookie是不可跨域名的。域名www.google.com颁发的Cookie不会被提交到域名www.baidu.com去。这是由Cookie的隐私安全机制决定的。隐私安全机制能够禁止网站非法获取其他网站的Cookie。

正常情况下,同一个一级域名下的两个二级域名如www.helloweenvsfei.com和images.helloweenvsfei.com也不能交互使用Cookie,因为二者的域名并不严格相同。如果想所有helloweenvsfei.com名下的二级域名都可以使用该Cookie,需要设置Cookie的domain参数。

// 新建Cookie
Cookie cookie = new Cookie("time","20220108"); 
// 设置域名
cookie.setDomain("www.westos.org");          
// 设置路径
cookie.setPath("/");                              
// 设置有效期
cookie.setMaxAge(Integer.MAX_VALUE);               
// 输出到客户端
response.addCookie(cookie);    

9.Cookie的不可跨域名性

很多网站都会使用Cookie。例如,Google会向客户端颁发Cookie,Baidu也会向客户端颁发Cookie。那浏览器访问Google会不会也携带上Baidu颁发的Cookie呢?或者Google能不能修改Baidu颁发的Cookie呢?

答案是否定的。Cookie具有不可跨域名性。根据Cookie规范,浏览器访问Google只会携带Google的Cookie,而不会携带Baidu的Cookie。Google也只能操作Google的Cookie,而不能操作Baidu的Cookie。

Cookie在客户端是由浏览器来管理的。浏览器能够保证Google只会操作Google的Cookie而不会操作Baidu的Cookie,从而保证用户的隐私安全。浏览器判断一个网站是否能操作另一个网站Cookie的依据是域名。Google与Baidu的域名不一样,因此Google不能操作Baidu的Cookie。

需要注意的是,虽然网站images.google.com与网站www.google.com同属于Google,但是域名不一样,二者同样不能互相操作彼此的Cookie。

注意:用户登录网站www.google.com之后会发现访问images.google.com时登录信息仍然有效,而普通的Cookie是做不到的。这是因为Google做了特殊处理。

10.Cookie的路径

domain属性决定运行访问Cookie的域名,而path属性决定允许访问Cookie的路径(ContextPath)。例如,如果只允许/myWeb/下的程序使用Cookie,可以这么写。

// 新建Cookie
Cookie cookie = new Cookie("time","20080808");     
// 设置路径
cookie.setPath("项目部署名称/myWeb/");                         
// 输出到客户端
response.addCookie(cookie);   

注意:页面只能获取它属于的Path的Cookie。例如/session/test/a.jsp不能获取到路径为/session/abc/的Cookie。使用时一定要注意。

11.Cookie的安全属性

HTTP协议不仅是无状态的,而且是不安全的。使用HTTP协议的数据不经过任何加密就直接在网络上传播,有被截获的可能。使用HTTP协议传输很机密的内容是一种隐患。如果不希望Cookie在HTTP等非安全协议中传输,可以设置Cookie的secure属性为true。浏览器只会在HTTPS和SSL等安全协议中传输此类Cookie。

下面的代码设置secure属性为true:

// 新建Cookie
Cookie cookie = new Cookie("time", "20080808"); 
// 设置安全属性
cookie.setSecure(true);                           
// 输出到客户端
response.addCookie(cookie);                        

提示:secure属性并不能对Cookie内容加密,因而不能保证绝对的安全性。如果需要高安全性,需要在程序中对Cookie内容加密、解密,以防泄密。

12.JavaScript操作Cookie

Cookie是保存在浏览器端的,因此浏览器具有操作Cookie的先决条件。浏览器可以使用脚本程序如JavaScript或者VBScript等操作Cookie。这里以JavaScript为例介绍常用的Cookie操作。例如下面的代码会输出本页面所有的Cookie。

//JavaScript 中,创建 cookie
document.cookie="username=John Doe";
//cookie 添加一个过期时间(以 UTC 或 GMT 时间)。默认情况下,cookie 在浏览器关闭时删除:
document.cookie="username=John Doe; expires=Thu, 18 Dec 2043 12:00:00 GMT";
//可以使用 path 参数告诉浏览器 cookie 的路径。默认情况下,cookie 属于当前页面。
document.cookie="username=John Doe; expires=Thu, 18 Dec 2043 12:00:00 GMT; path=/";
//javascript读取 cookie
var x = document.cookie;

由于JavaScript能够任意地读写Cookie,有些好事者便想使用JavaScript程序去窥探用户在其他网站的Cookie。不过这是徒劳的,W3C组织早就意识到JavaScript对Cookie的读写所带来的安全隐患并加以防备了,W3C标准的浏览器会阻止JavaScript读写任何不属于自己网站的Cookie。换句话说,A网站的JavaScript程序读写B网站的Cookie不会有任何结果。

13.永久登录(重要、重要)

如果用户是在自己家的电脑上上网,登录时就可以记住他的登录信息,下次访问时不需要再次登录,直接访问即可。实现方法是把登录信息如账号、密码等保存在Cookie中,并控制Cookie的有效期,下次访问时再验证Cookie中的登录信息即可。

保存登录信息有多种方案。最直接的是把用户名与密码都保持到Cookie中,下次访问时检查Cookie中的用户名与密码,与数据库比较。这是一种比较危险的选择,一般不把密码等重要信息保存到Cookie中

还有一种方案是把密码加密后保存到Cookie中,下次访问时解密并与数据库比较。这种方案略微安全一些。如果不希望保存密码,还可以把登录的时间戳保存到Cookie与数据库中,到时只验证用户名与登录时间戳就可以了。

这几种方案验证账号时都要查询数据库。

本例将采用另一种方案,只在登录时查询一次数据库,以后访问验证登录信息时不再查询数据库。实现方式是把账号按照一定的规则加密后,连同账号一块保存到Cookie中。下次访问时只需要判断账号的加密规则是否正确即可。把账号保存到名为account的Cookie中,把账号连同密钥用MD5算法加密后保存到名为ssid的Cookie中。验证时验证Cookie中的账号与密钥加密后是否与Cookie中的ssid相等。

写Cookie

 // 获取account参数
String account =request.getParameter("account");
 // 获取password参数                                                  
String password =request.getParameter("password");
// 获取timeout参数
int timeout = newInteger(request.getParameter("timeout"));
// 把账号、密钥使用MD5加密后保存
String ssid =MDigest5.md5(account + MDigest5.KEY);
// 新建Cookie
CookieaccountCookie = new Cookie("account", account); 
 // 设置有效期
accountCookie.setMaxAge(Integer.MAX_VALUE);   
// 新建Cookie
Cookie ssidCookie =new Cookie("ssid", ssid);   
// 设置有效期
ssidCookie.setMaxAge(Integer.MAX_VALUE);                 
// 输出到客户端
response.addCookie(accountCookie);             
// 输出到客户端
response.addCookie(ssidCookie);            
 // 重新请求本页面,参数中带有时间戳,禁止浏览器缓存页面内容
response.sendRedirect(request.getRequestURI() + "?" + System.
        currentTimeMillis());
return;

下次访问时只需要判断账号的加密规则是否正确

// 是否登录
boolean login = false;                        
// 账号
String account = null;                       
// SSID标识
String ssid = null;                           
// 如果Cookie不为空
if(request.getCookies() !=null){               
	// 遍历Cookie
    for(Cookie cookie :request.getCookies()){  
		// 如果Cookie名为 account
       if(cookie.getName().equals("account"))  
         // 保存account内容                                        
         account = cookie.getValue();       
		 
       if(cookie.getName().equals("ssid")) 
 		  // 保存SSID内容
           ssid = cookie.getValue();         
        }
    }
    // 如果account、SSID都不为空
    if(account != null && ssid !=null){
        // 如果加密规则正确, 则视为已经登录
        login =ssid.equals(MDigest5.md5(account + MDigest5.KEY));
    }
}

MD5加密

package com.xawl.util;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MDigest5 {
	//加密盐
	private static final String KEY="west8309";
	/**
	 * 返回MD5加密串(默认32位)
	 * @param plainText 需要加密的字符串
	 * @return 返回MD5加密串
	 */
	private static String md5(String plainText) {
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			md.update(plainText.getBytes());
			byte b[] = md.digest();
			int i;
			StringBuffer buf = new StringBuffer("");
			for (int offset = 0; offset < b.length; offset++) {
				i = b[offset];
				if (i < 0)
					i += 256;
				if (i < 16)
					buf.append("0");
				buf.append(Integer.toHexString(i));
			}
			return buf.toString();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			return null;
		}
	}
}
<%@ include file="ckuserlogin.jsp"%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
恭喜登录成功!
</body>
</html>
<%@ page import="com.xawl.util.MDigest5" %>
<%
    //登录成功后访问的页面(只有登录成功后才以访问页面),需要做登录状态的认证。
    String accout = "";
    String ssid = "";
    boolean isLogin = false;
    Cookie[] cookies = request.getCookies();
    if(cookies!=null)
    for (Cookie cookie:cookies) {
        //获得账号
        if("act".equals(cookie.getName())){
            accout = cookie.getValue();
        }
        if("ssid".equals(cookie.getName())){
            ssid = cookie.getValue();
        }
    }
    if(!"".equals(accout) && !"".equals(ssid)){
        isLogin = ssid.equals(MDigest5.getMD5(accout+MDigest5.KEY));
    }
    if(isLogin==false){
        response.sendRedirect("login.jsp");
        return;
    }
%>
package  com.xawl.controller;

import com.xawl.iservice.IUserService;
import  com.xawl.pojo.Userinfo;
import com.xawl.service.UserService;
import  com.xawl.util.MDigest5;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.SQLException;

@WebServlet(name = "LoginController",urlPatterns = "/dologin")
public class LoginController extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        /**
         * 实现思路
         * 1.设置编码,设置响应类型编码,解决乱码问题
         * 2.获得数据(用户名、密码、验证码)
         * 3.验证数据的合法性,不合格返回登录页面
         * 4.到数据库查找用户名
         * 5.处理返回结果,登录成功的话,转向成页面,不成功返回登录页面提示用户
         */
        //1.设置编码,设置响应类型编码,解决乱码问题
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        //2.获得数据(用户名、密码、验证码)
        String username = request.getParameter("username");
        String userpwd = request.getParameter("userpwd");
        String usercode = request.getParameter("usercode");
        //3.验证数据的合法性,不合格返回登录页面
        //定义出错的标志位
        boolean isErr = false;
        if(username==null || "".equals(username.trim())){
            request.setAttribute("userErr","*用户不能为空");
            isErr = true;
        }
        if(userpwd==null || "".equals(userpwd.trim())){
            request.setAttribute("pwdErr","*密码不能为空");
            isErr = true;
        }
        if(usercode==null || "".equals(usercode.trim())){
            request.setAttribute("codeErr","*验证码不能为空");
            isErr = true;
        }
        else{
            Object usercode1 = request.getSession().getAttribute("usercode");
            //判断验证码是否失效
            if(usercode1==null){
                request.setAttribute("codeErr","*验证码失效,请重新生成验证码");
                isErr = true;
            }else {
                //判断验证码是否正确
                String mycode = usercode1.toString().replace(" ","");
                if(!usercode.equalsIgnoreCase(mycode)){
                    request.setAttribute("codeErr","*验证码不正确");
                    isErr = true;
                }
            }
        }
        //如果不合格,返回登录页面
        if(isErr){
            request.getRequestDispatcher("login.jsp").forward(request,response);
            return;
        }
        //4.到数据库查找用户名
        IUserService userService = new UserService();
        try {
            Userinfo user = userService.findByUsername(username);
            //判断是否存在此用户
            if(user==null){ //此用户不存在
                request.setAttribute("userErr","*用户名不正确");
                isErr = true;
            }
            else{//通过用户名查找到了此用户
                //判断用户是否被锁定
                if(user.getUserstate()!=1){//锁定
                    request.setAttribute("userErr","*此用户名已被锁定");
                    isErr = true;
                }
                else {
                    //密码加密MD5(非对称,不可逆)
                    String usermd5 = MDigest5.getMD5(userpwd + MDigest5.KEY);
                    if (user.getUserpwd().equals(usermd5)) {//密码正确
                        // 用cookie保存登录状态
                        saveUserLoginSate(user,response);
                        //登录成功,地址重定向
                        response.sendRedirect("main.jsp");
                        return;
                    } else {
                        request.setAttribute("pwdErr", "*密码不正确");
                        isErr = true;
                    }
                }
            }
        } catch (SQLException ex) {
            ex.printStackTrace();
            request.setAttribute("loginErr","*连接服务器出错");
            isErr = true;
        }
        System.out.println("bbbbbbbbbbbbbb");
        //登录不成功
        if(isErr){
            request.getRequestDispatcher("login.jsp").forward(request,response);
            return;
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    /**
     * 保存用户状态
     * @param user
     */
    public void saveUserLoginSate(Userinfo user,HttpServletResponse response){
        //账号
        String username = user.getUsername();
        //登录时间
        String loginTime =System.currentTimeMillis()+"";
        //密钥
        String ssid = MDigest5.getMD5(username+MDigest5.KEY);

        //创建账号Cookie
        Cookie account_ck = new Cookie("act",username);
        //设置过期(永久)
        account_ck.setMaxAge(Integer.MAX_VALUE);
        //写入
        response.addCookie(account_ck);

        //创建登录时间Cookie
        Cookie ltime_ck = new Cookie("ltime",loginTime);
        //设置过期(永久)
        ltime_ck.setMaxAge(Integer.MAX_VALUE);
        //写入
        response.addCookie(ltime_ck);

        //创建密钥Cookie
        Cookie ssid_ck = new Cookie("ssid",ssid);
        //设置过期(永久)
        ssid_ck.setMaxAge(Integer.MAX_VALUE);
        //写入
        response.addCookie(ssid_ck);
    }
}
  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Geek Li

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值