HTTP协议是无状态的的协议。一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接。Cookie和Seesion,就是这样的机制,可以弥补HTTP协议的无状态的不足。
Cookie是客户端技术,程序把每个客户的数据以Cookie的形式写给用户各自的浏览器。当用户私用浏览器再去访问服务器中的web资源时,就会带着各自的数据去。这样web资源处理的就是用户各自的数据了。
Session是服务器端技术,服务器在运行时可以为每个用户的浏览器创建一个其独享的Session对象,由于Session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的资源放在各自的Session中,当用户再去访问服务器中的其他web资源时,其他web资源再从用户各自的Session中取出数据为用户服务。
一、Cookie基础
1、工作原理:
HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户的身份。Cookie就像是给客户端颁发的一个通行证。每个客户浏览器一个,无论谁访问必须携带自己的通行证。这样服务器就能从通行证上确认客户身份了。
2、基本知识:
1)Cookie是保存在客户端的内容,每个浏览器有各自的编号,区分浏览器,浏览器不同,信息不共享。
2)写入的信息只能是文本文档。不支持中文,如支持,必须使用从新转码。
3)客户端可以阻止服务器写入。
4)Cookie不能跨域名。服务器只能拿自己的web应用写入的东西。
5)浏览器的Cookie数量是有限的,大概是30-50个,每个为4K左右,每个浏览器各有不同。
3、实现:
1)客户端请求服务器,可以使用 response 向客户端浏览器颁发一个Cookie。客户端会把Cookie保存起来。当浏览器再去访问该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查Cookie,以此来辨认用户状态。服务器还可以根据需要修改 Cookie 的内容。
2)可以在浏览器地址栏数据 javascript:alert(document.cookie) 就可以了。document.cookie获取所有的。
二、Cookie操作
1、基本知识:
1)java中把Cookie封装成javax.servlet.http.Cookie类。服务器通过操作Cookie类对象对客户端Cookie进行操作。
2)Cookie对象使用key-value 属性对的形式保存用户状态。一个 request 或者 response 同时使用多个Cookie。
3)创建Cookie :Cookie cookie = new Cookie(String key, String value)。如果key和value为中文(Unicode字符),需要使用URLEncoder进行编码。如果为二进制,则需要使用Base64编码。
- // 1-创建Cookie
- String key = "aaa";
- String value = "111";
- Cookie cookie = new Cookie(key, value);
- // 1-2 创建中文, 使用URLEncoder编码 和 URLDecoder解码
- Cookie cookieChina = new Cookie(URLEncoder.encode("中文", "UTF-8"), URLEncoder.encode("中文", "UTF-8"));
4)向客户端设置Cookie : response.addCookie(Cookie cookie) 。
5)获取客户端的Cookie : Cookie[] cookies = request.getCookies()(以Cookie[]数组形式返回)。
6)获取Cookie 的name和value: String tName = Cookie.getName(); String tValue = Cookie.getValue();
- // 3-从客户端获取
- Cookie[] cookies = request.getCookies();
- if (cookies != null && cookies.length < 0) {
- for (Cookie tCookie : cookies) {
- String tName = tCookie.getName();
- String tValue = tCookie.getValue();
- int maxAge = tCookie.getMaxAge();//有效期
- logger.debug("Cookie的name : " + tName + " | Cookie的value : " + tValue+ " | Cookie的有效期: "+ maxAge);
- }
- }
7)设置Cookie的时间 : cookie.setMaxAge(maxAge); 值的类型:
A、正数:表示Cookie在maxAge之后失效,cookieChina.setMaxAge(Integer.MAX_VALUE)-永久有效
B、负数(-1):临时Cookie,浏览器关闭即失效,不保存。-1是默认值。
C、0-删除该Cookie。Cookie没有删除的方法,可以设置maxage为0;修改Cookie: 可以添加一个同名的Cookie用于覆盖前一个来修改Cookie。
- // 4-Cookie的删除与修改
- Cookie cookie2 = new Cookie("time","201401008");
- cookie2.setMaxAge(0);//删除Cookie,设置maxage=0即可删除
- response.addCookie(cookie2);
- response.addCookie(new Cookie("time","20150909"));//同名Cookie,修改前一个cookie的作用
8)Cookie的域名: Domain属性(IE是禁止的,会有安全问题)。
A、Cookie具有不可跨域名性。www.gogle.com 和 www.baidu.com不可交互Cookie。同一域名下两个二级域名 如 http://www.baidu.com 和 http://music.baidu.com 也 不同交互使用Cookie。如果想所有的 baidu.com 名下的二级域名都可以使用该 Cookie, 需要设置Domain属性
B、Domain属性,必须以(“.”)开始。另外,name相同但是domain 不同的两个Cookie是两个不同的Cookie。
C、如果想要两个域名完全不同的网站共有 Cookie, 可以生成两个Cookie, domain属性分别为两个域名,输出到客户端。
- // 5-Cookie的域名
- Cookie cookie3 = new Cookie("time","201401008");
- cookie3.setDomain(".baidu.com");//设置域名,意思是所有bai.com下的二级域名都可以访问。必须以“.”开始
- cookie3.setPath("/");//设置所有路径都可以使用
- cookie3.setMaxAge(Integer.MAX_VALUE); //设置有效期
- response.addCookie(cookie3);
9)Cookie的路径: path属性
A、path属性决定了访问Cookie的路径,即contextPath路径。例如,如果只允许/JavaWeb/下的程序使用Cookie,可以这么写:
B、path属性必须以“/XXX/”这种形式,当只有“/”时,表示所有路径都可以访问该Cookie。
C、一个Servlet/JSP 设置的cookie 只能被与这个Servlet/JSP在同一路径下或子路径下的Servlet/JSP访问。父路径和其他路径无法获取。如sesson/test/b.jsp可以获取到路径session/a.jsp的 Cookie,而不能获取/session/b/的Cookie。
D、这边的路径不是指文件的存放真实路径,而是指url路径,是指<url-pattern>设置的路径。
- // 6-Cookie的路径
- Cookie cookie4 = new Cookie("time","201401008");
- cookie4.setPath("/Javaweb/");//只有Javaweb/下的可以访问
- response.addCookie(cookie4);
10)Cookie的安全属性:secure属性
如果不希望Cookie在HTTP等非安全协议中传输,可以设置cookie的secure属性属性为true。浏览器只会在HTTPS和SSL等安全协议中传输此类Cookie。
- // 7-Cookie的安全属性
- Cookie cookie5 = new Cookie("time","201401008");
- cookie5.setSecure(true);//true,在HTTP中无法获取本Cookie
2、JS操作Cookie:
1)格式:在js中Cookie的格式:account=111; ssid=222; JSESSIONID=7E99EBDC3CA63AB849D1493FBCCC5159,类似于map类型。
2)获取:document.cookie获取所有的Cookie。
3)获取单个固定的Cookie,可以自己封装方法:
- /**
- * 获取Cookie
- */
- var getCookie = function(name) {
- var str = document.cookie;
- if (!str || str.indexOf(name + "=") < 0)
- return;
- var cookies = str.split("; ");
- for (var i = 0; i < cookies.length; i++) {
- var cookie = cookies[i];
- if (cookie.indexOf(name + "=") == 0) {
- var value = cookie.substring(name.length + 1);
- return decodeURI(value);
- }
- }
- };
4)、创建Cookie的方法,我们也可以封装成一个方法:
- /**
- * 设置Cookie
- */
- var setCookie = function(name, value) {
- // document.cookie = name + "=" + encodeURI(value);
- var expires = (arguments.length > 2) ? arguments[2] : null;
- var path = (arguments.length > 3) ? arguments[3] : null;
- var domain = (arguments.length > 4) ? arguments[4] : null;
- var secure = (arguments.length > 5) ? arguments[5] : false;
- document.cookie = name
- + "="
- + encodeURI(value)
- + ((expires == null) ? "" : ("; expires=" + expires
- .toGMTString()))
- + ((path == null) ? "" : ("; path=" + path))
- + ((domain == null) ? "" : ("; domain=" + domain))
- + ((secure == true) ? "; secure" : "");
- };
3、Jquery操作Cookie:需要下载jquery.cookie.js,使用起来非常方便。
1)简单设置Cookie : 设置一个name-Cookie的名字,value-Cookie的值。
- $.cookie("name", "value");
2)设置其他参数:
- //设置完整的cookie
- $.cookie("name", "sam-sho",{
- expires : 10,//有效期,单位为天
- path : "/",
- domain : "jquery.com"
- secrue : true
- });
- //设置精细的时间
- var cookietime = new Date();
- cookietime.setTime(date.getTime() + (60 * 60 * 1000));//coockie保存一小时
- $.cookie("sam", "sam-sho",{expires:cookietime});
3)读取Cookie :读取名字为sam的Cookie。
- $.cookie("sam")
4)删除Cookie :删除名称为 sam 的Cookie,只要把value设置成null。注意:必须使用与之前设置的相同的路径(path)和域名(domain),才能正确删除。
- $.cookie("sam",null);
三、实际案例。
1、实例1::永久登录。
1)纯JSP实现:
- <%@ page language="java" pageEncoding="UTF-8" isErrorPage="false" %>
- <jsp:directive.page import="java.security.MessageDigest"/>
- <%!
- // 密钥
- private static final String KEY = ":cookie@helloweenvsfei.com";
- // MD5 加密算法
- public final static String calcMD5(String ss) {
- String s = ss==null ? "" : ss;
- char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
- try {
- byte[] strTemp = s.getBytes();
- MessageDigest mdTemp = MessageDigest.getInstance("MD5");
- mdTemp.update(strTemp);
- byte[] md = mdTemp.digest();
- int j = md.length;
- char str[] = new char[j * 2];
- int k = 0;
- for (int i = 0; i < j; i++) {
- byte byte0 = md[i];
- str[k++] = hexDigits[byte0 >>> 4 & 0xf];
- str[k++] = hexDigits[byte0 & 0xf];
- }
- return new String(str);
- } catch (Exception e) {
- return null;
- }
- }
- %>
- <%
- request.setCharacterEncoding("UTF-8");
- response.setCharacterEncoding("UTF-8");
- String action = request.getParameter("action");
- if("login".equals(action)){
- String account = request.getParameter("account");
- String password = request.getParameter("password");
- int timeout = new Integer(request.getParameter("timeout"));
- // 把帐号连同密钥使用MD5后加密后保存
- String ssid = calcMD5(account + KEY);
- // 把帐号保存到Cookie中 并控制有效期
- Cookie accountCookie = new Cookie("account", account);
- accountCookie.setMaxAge(timeout);
- // 把加密结果保存到Cookie中 并控制有效期
- Cookie ssidCookie = new Cookie("ssid", ssid);
- ssidCookie.setMaxAge(timeout);
- response.addCookie(accountCookie);
- response.addCookie(ssidCookie);
- // 重新请求本页面
- response.sendRedirect(request.getRequestURI() + "?" + System.currentTimeMillis());
- return;
- }
- else if("logout".equals(action)){
- // 删除Cookie中的帐号
- Cookie accountCookie = new Cookie("account", "");
- accountCookie.setMaxAge(0);
- // 删除Cookie中的加密结果
- Cookie ssidCookie = new Cookie("ssid", "");
- ssidCookie.setMaxAge(0);
- response.addCookie(accountCookie);
- response.addCookie(ssidCookie);
- // 重新请求本页面
- response.sendRedirect(request.getRequestURI() + "?" + System.currentTimeMillis());
- return;
- }
- boolean loggin = false;
- String account = null;
- String ssid = null;
- // 获取Cookie中的account与ssid
- if(request.getCookies() != null){
- for(Cookie cookie : request.getCookies()){
- if(cookie.getName().equals("account"))
- account = cookie.getValue();
- if(cookie.getName().equals("ssid"))
- ssid = cookie.getValue();
- }
- }
- if(account != null && ssid != null){
- // 如果加密规则正确, 则视为已经登录
- loggin = ssid.equals(calcMD5(account + KEY));
- }
- %>
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <title><%= loggin ? "欢迎您回来" : "请先登录" %></title>
- <link rel="stylesheet" type="text/css" href="css/style.css">
- </head>
- <body>
- <div align="center" style="margin:10px; ">
- <fieldset>
- <legend>当前有效的 Cookie</legend>
- <script>document.write(document.cookie);</script>
- </fieldset>
- <fieldset>
- <legend><%= loggin ? "欢迎您回来" : "请先登录" %></legend>
- <% if(loggin){ %>
- 欢迎您, ${ cookie.account.value }.
- <a href="${ pageContext.request.requestURI }?action=logout">注销</a>
- <% } else { %>
- <form action="${ pageContext.request.requestURI }?action=login" method="post">
- <table>
- <tr>
- <td>
- 帐号:
- </td>
- <td>
- <input type="text" name="account" style="width:200px; ">
- </td>
- </tr>
- <tr>
- <td>
- 密码:
- </td>
- <td>
- <input type="password" name="password" style="width:200px; ">
- </td>
- </tr>
- <tr>
- <td>
- 有效期:
- </td>
- <td>
- <input type="radio" name="timeout" value="-1" checked> 关闭浏览器即失效 <br/>
- <input type="radio" name="timeout" value="<%= 30 * 24 * 60 * 60 %>"> 30天内有效 <br/>
- <input type="radio" name="timeout" value="<%= Integer.MAX_VALUE %>"> 永久有效 <br/>
- </td>
- </tr>
- <tr>
- <td>
- </td>
- <td>
- <input type="submit" value=" 登 录 " class="button">
- </td>
- </tr>
- </table>
- </form>
- <% } %>
- </fieldset>
- </div>
- </body>
- </html>
2、实例2: 使用Cookie统计UV量。2)使用Servlet实现较为麻烦,MD5的公共方法等略。如下:
A、LoginCookieServlet: 负责记录Cookie的servlet。Post提交表单后,servlet把写两个cookie到客户端。一是账户的cookie,另一个是账户加密后的cookie。然后重定向到CheckCookieServlet,校验。一定要用重定向,确保客户端使用两次请求。由于加密的key是服务器独有的,是安全的。
- package servlet.cookie;
- import java.io.IOException;
- import javax.servlet.ServletException;
- import javax.servlet.http.Cookie;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.apache.log4j.Logger;
- import util.MD5Utils;
- /**
- *
- * LoginCookieServlet.java
- *
- * @title 永久登录 登录的servlet
- * @description
- * @author SAM-SHO
- * @Date 2014-10-10
- */
- public class LoginCookieServlet extends HttpServlet {
- private static final long serialVersionUID = 1L;
- private Logger logger = Logger.getLogger(this.getClass());
- // 密钥
- private static final String KEY = ":sam-sho@sinosoft.com";//可以放配置文件
- public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- this.doPost(request, response);
- }
- /**
- * post方法访问,处理永久登录
- */
- public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- request.setCharacterEncoding("UTF-8");
- response.setCharacterEncoding("UTF-8");
- String action = request.getParameter("action");
- if ("login".equals(action)) {
- String account = request.getParameter("account");
- // String password = request.getParameter("password");
- int timeout = new Integer(request.getParameter("timeout"));
- // 把帐号连同密钥使用MD5后加密后保存
- String ssid = MD5Utils.calcMD5(account + KEY);
- // 把帐号保存到Cookie中 并控制有效期
- Cookie accountCookie = new Cookie("account", account);//中文需要编码
- accountCookie.setMaxAge(timeout);
- // 把加密结果保存到Cookie中 并控制有效期
- Cookie ssidCookie = new Cookie("ssid", ssid);
- ssidCookie.setMaxAge(timeout);
- response.addCookie(accountCookie);// 账户Cookie
- response.addCookie(ssidCookie);// 加密后账户的Cookie
- logger.debug(accountCookie.getName()+"++++++++"+accountCookie.getValue());
- logger.debug(ssidCookie.getName()+"+++++++"+ ssidCookie.getValue());
- // 重定向到校验servlet
- // 使用两个request,这样访问到校验的request就带有了本次response写入的cookie。
- response.sendRedirect(this.getServletContext().getContextPath() + "/servlet/CheckCookieServlet");
- return;
- }
- }
- }
B、LoginoutCookieServlet:注销的servlet,负责删除cookie。然后校验。其实可以和LoginCookieServlet进行合并。
- package servlet.cookie;
- import java.io.IOException;
- import javax.servlet.ServletException;
- import javax.servlet.http.Cookie;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- /**
- *
- * LoginoutCookieServlet
- *
- * @title 永久登录-注销的servlet
- * @description
- * @author SAM-SHO
- * @Date 2014-10-10
- */
- public class LoginoutCookieServlet extends HttpServlet {
- private static final long serialVersionUID = 1L;
- public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- this.doPost(request, response);
- }
- /**
- * post方法访问,处理注销,删除Cookie
- */
- public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- request.setCharacterEncoding("UTF-8");
- response.setCharacterEncoding("UTF-8");
- String action = request.getParameter("action");
- if ("logout".equals(action)) {
- // 删除Cookie中的帐号
- Cookie accountCookie = new Cookie("account", "");
- accountCookie.setMaxAge(0);
- // 删除Cookie中的加密结果
- Cookie ssidCookie = new Cookie("ssid", "");
- ssidCookie.setMaxAge(0);
- response.addCookie(accountCookie);
- response.addCookie(ssidCookie);
- // 重定向到校验servlet
- response.sendRedirect(this.getServletContext().getContextPath() + "/servlet/CheckCookieServlet");
- return;
- }
- }
- }
C、CheckCookieServlet:负责校验。
- package servlet.cookie;
- import java.io.IOException;
- import java.util.Arrays;
- import javax.servlet.ServletException;
- import javax.servlet.http.Cookie;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.apache.log4j.Logger;
- import util.MD5Utils;
- /**
- *
- * CheckCookieServlet.java
- *
- * @title 用于校验
- * @description
- * @author SAM-SHO
- * @Date 2014-10-11
- */
- public class CheckCookieServlet extends HttpServlet {
- private static final long serialVersionUID = 1L;
- private Logger logger = Logger.getLogger(this.getClass());
- // 密钥
- private static final String KEY = ":sam-sho@sinosoft.com";//可以放配置文件
- public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- this.doPost(request, response);
- }
- /**
- * 校验
- */
- public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- request.setCharacterEncoding("UTF-8");
- response.setCharacterEncoding("UTF-8");
- boolean loggin = false;
- String account = null;
- String ssid = null;
- // 获取Cookie中的account与ssid
- Cookie[] cookies = request.getCookies();
- if(cookies!= null){
- logger.debug("所有的Cookies : " + Arrays.asList(cookies));
- for(Cookie cookie : cookies){
- if((cookie.getName()).equals("account"))
- account = cookie.getValue();
- if((cookie.getName()).equals("ssid"))
- ssid = cookie.getValue();
- }
- }
- if(account != null && ssid != null){
- // 如果加密规则正确, 则视为已经登录
- loggin = ssid.equals(MD5Utils.calcMD5(account + KEY));
- }
- logger.debug("是否匹配 : "+ loggin);
- request.getSession(true).setAttribute("loggin", loggin);
- // 跳转到页面
- request.getRequestDispatcher("/view/cookie/jsp/loginCookie.jsp").forward(request, response);
- // response.sendRedirect(this.getServletContext().getContextPath() +"/view/cookie/jsp/loginCookie.jsp");
- return;
- }
- }
D、loginCookie.jsp:显示页面。
- <%@ page language="java" pageEncoding="UTF-8" isErrorPage="false"%>
- <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
- <c:set var="base" value="${pageContext.request.contextPath}" />
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <title>
- <c:choose>
- <c:when test="${loggin==true}">欢迎您回来</c:when>
- <c:otherwise>请先登录</c:otherwise>
- </c:choose>
- </title>
- <link rel="stylesheet" type="text/css" href="${base}/css/style.css">
- <script type="text/javascript" src="${base}/js/jquery-1.7.2.js"></script>
- <script type="text/javascript" src="${base}/view/cookie/js/cookie.js"></script>
- </head>
- <body>
- <div align="center" style="margin: 10px;">
- <fieldset>
- <legend>
- 当前有效的 Cookie
- </legend>
- <script>
- document.write(document.cookie);
- </script>
- </fieldset>
- <fieldset>
- <legend>
- <c:choose>
- <c:when test="${loggin ==true}">
- 欢迎您回来
- </c:when>
- <c:otherwise>
- 请先登录
- </c:otherwise>
- </c:choose>
- </legend>
- <c:choose>
- <c:when test="${loggin==true}">
- 欢迎您, ${cookie.account.value }
- <a href="${base}/servlet/LoginoutCookieServlet?action=logout">注 销</a>
- </c:when>
- <c:otherwise>
- <form action="${base}/servlet/LoginCookieServlet?action=login"
- method="post">
- <table>
- <tr>
- <td>
- 帐号:
- </td>
- <td>
- <input type="text" name="account" style="width: 200px;">
- </td>
- </tr>
- <tr>
- <td>
- 密码:
- </td>
- <td>
- <input type="password" name="password" style="width: 200px;">
- </td>
- </tr>
- <tr>
- <td>
- 有效期:
- </td>
- <td>
- <input type="radio" name="timeout" value="-1" checked>
- 关闭浏览器即失效
- <br />
- <input type="radio" name="timeout"
- value="<%=30 * 24 * 60 * 60%>">
- 30天内有效
- <br />
- <input type="radio" name="timeout"
- value="<%=Integer.MAX_VALUE%>">
- 永久有效
- <br />
- </td>
- </tr>
- <tr>
- <td>
- </td>
- <td>
- <input type="submit" value=" 登 录 " class="button">
- </td>
- </tr>
- </table>
- </form>
- </c:otherwise>
- </c:choose>
- </fieldset>
- </div>
- </body>
- <script type="text/javascript">
- var account = CookieJs.getCookie("account");
- var ssid = CookieJs.getCookie("ssid");
- alert(account);
- alert(ssid);
- </script>
- </html>
- // 客户标识
- private final String CUSTOMER_IDENTIFICATION = "CusID";
- private String uvFlag;
- /**
- * cookie中获取用户唯一标识 采用uuid记录 存在获取不存在重新设置
- */
- private void autoSetCookie() {
- Cookie[] cookies = getRequest().getCookies();
- int count = 0;
- if (cookies != null) {
- for (Cookie cookie : cookies) {
- if (org.apache.commons.lang3.StringUtils.equals(
- cookie.getName(), CUSTOMER_IDENTIFICATION)) {
- uvFlag = cookie.getValue();//存入数据库,以便统计
- break;
- }
- count++;
- // 该cookie在cookie列表中不存在
- if (count == cookies.length) {
- autoSetCid();
- }
- }
- } else {
- autoSetCid();
- }
- }
- /**
- * 自动设置客户端唯一标识
- */
- private void autoSetCid() {
- uvFlag = UUID.randomUUID().toString().replaceAll("[-]", "");
- Cookie cidCookie = new Cookie(CUSTOMER_IDENTIFICATION, uvFlag);
- cidCookie.setDomain(PropertyFileUtil.get("domain"));//写在配置文件
- cidCookie.setMaxAge(365 * 24 * 60 * 60);
- cidCookie.setPath("/");
- getResponse().addCookie(cidCookie);//向客户端写Cookie
- }