欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

cookie和session详解java版本

程序员文章站 2024-02-28 20:00:22
...

https://www.cnblogs.com/andy-zhou/p/5360107.html

什么是cookie
Cookie就是由服务器发给客户端的特殊信息,而这些信息以文本文件的方式存放在客户端,然后客户端每次向服务器发送请求的时候都会带上这些特殊的信息。

为什么要有cookie:保持会话跟踪。
在程序中,会话跟踪是很重要的事情。理论上,一个用户的所有请求操作都应该属于同一个会话,而另一个用户的所有请求操作则应该属于另一个会话,二者不能混淆。而Web应用程序是使用HTTP协议传输数据的。HTTP协议是无状态的协议。一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接。Cookie就是这样的一种机制。它可以弥补HTTP协议无状态的不足。在Session出现之前,基本上所有的网站都采用Cookie来跟踪会话。

怎么用cookie:
有两个http头部是专门负责设置以及发送cookie的,它们分别是Set-Cookie以及Cookie。当服务器返回给客户端一个http响应信息时,其中如果包含Set-Cookie这个头部时,意思就是指示客户端建立一个cookie,并且在后续的http请求中自动发送这个cookie到服务器端,直到这个cookie过期。

cookie的生存期:
如果cookie的生存时间是整个会话期间的话,那么浏览器会将cookie保存在内存中,浏览器关闭时就会自动清除这个cookie,另外一种情况就是保存在客户端的硬盘中。浏览器关闭的话,该cookie也不会被清除,下次打开浏览器访问对应网站时,这个cookie就会自动再次发送到服务器端。

注意:Cookie功能需要浏览器的支持。如果浏览器不支持Cookie(如大部分手机中的浏览器)或者把Cookie禁用了,Cookie功能就会失效。不同的浏览器采用不同的方式保存Cookie。IE浏览器会在“C:\Documents and Settings\你的用户名\Cookies”文件夹下以文本文件形式保存,一个文本文件保存一个Cookie。

用GET和POST传递数据比用cookies来传递更稳定

Cookie具有不可跨域名性。根据Cookie规范,浏览器访问Google只会携带Google的Cookie,而不会携带Baidu的Cookie。Google也只能操作Google的Cookie,而不能操作Baidu的Cookie。domain属性决定运行访问Cookie的域名,而path属性决定允许访问Cookie的路径(ContextPath)。例如,如果只允许/sessionWeb/下的程序使用Cookie,可以这么写:

Cookie cookie = new Cookie("time","20080808"); // 新建Cookie
cookie.setPath("/session/"); // 设置路径
response.addCookie(cookie); // 输出到客户端

设置为“/”时允许所有路径使用Cookie。path属性需要使用符号“/”结尾。name相同但domain不同的两个Cookie也是两个不同的Cookie。

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

  1. domain表示的是cookie所在的域,默认为请求的地址,如网址为www.test.com/test/test.aspx,那么domain默认为www.test.com。而跨域访问,如域A为t1.test.com,域B为t2.test.com,那么在域A生产一个令域A和域B都能访问的cookie就要将该cookie的domain设置为.test.com;如果要在域A生产一个令域A不能访问而域B能访问的cookie就要将该cookie的domain设置为t2.test.com。
  2. path表示cookie所在的目录,默认为/,就是根目录。在同一个服务器上有目录如下:/test/,/test/cd/,/test/dd/,现设一个cookie1的path为/test/,cookie2的path为/test/cd/,那么test下的所有页面都可以访问到cookie1,而/test/和/test/dd/的子页面不能访问cookie2。这是因为cookie能让其path路径下的页面访问。
  3. 浏览器会将domain和path都相同的cookie保存在一个文件里,cookie间用*隔开。

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

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

Java中的cookie内容:
key-value对,用BASE64编码保存二进制图片。Unicode编码保存中文。
常用属性:name/value

String name:该Cookie的名称。Cookie一旦创建,名称便不可更改。 
Object value:该Cookie的值。如果值为Unicode字符,需要为字符编码。如果值为二进制数据,则需要使用BASE64编码。 
int maxAge:该Cookie失效的时间,单位秒。如果为正数,则该Cookie在>maxAge秒之后失效。如果为负数,该Cookie为临时Cookie,关闭浏览器即失效,浏览器也不会以任何形式保存该Cookie。如果为0,表示删除该Cookie。默认为–1。 
boolean secure:该Cookie是否仅被使用安全协议传输。安全协议。安全协议有HTTPS,SSL等,在网络上传输数据之前先将数据加密。默认为false。 
String path:该Cookie的使用路径。如果设置为“/sessionWeb/”,则只有contextPath为“/sessionWeb”的程序可以访问该Cookie。如果设置为“/”,则本域名下contextPath都可以访问该Cookie。注意最后一个字符必须为“/”。
String domain:可以访问该Cookie的域名。如果设置为“.google.com”,则所有以“google.com”结尾的域名都可以访问该Cookie。注意第一个字符必须为“.”。
 String comment:该Cookie的用处说明。浏览器显示Cookie信息的时候显示该说明。 int version:该Cookie使>用的版本号。0表示遵循Netscape的Cookie规范,1表示遵循W3C的RFC 2109规范。

注意:从客户端读取Cookie时,包括maxAge在内的其他属性都是不可读的,也不会被提交。浏览器提交Cookie时只会提交name与value属性。maxAge属性只被浏览器用来判断Cookie是否过期。

由于浏览器每次请求服务器都会携带Cookie,因此Cookie内容不宜过多,否则影响速度。Cookie的内容应该少而精。

安全:
如果不希望Cookie在HTTP等非安全协议中传输,可以设置Cookie的secure属性为true。浏览器只会在HTTPS和SSL等安全协议中传输此类Cookie。secure属性并不能对Cookie内容加密,因而不能保证绝对的安全性。如果需要高安全性,需要在程序中对Cookie内容加密、解密,以防泄密。

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

js读取cookie:

<script>document.write(document.cookie);</script>

注意:W3C标准的浏览器会阻止JavaScript读写任何不属于自己网站的Cookie。换句话说,A网站的JavaScript程序读写B网站的Cookie不会有任何结果。

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

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

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

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

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

<%@ page language="java" pageEncoding="UTF-8" isErrorPage="false" %>
<%!                                                  // JSP方法
    private static final String KEY =":[email protected]"; // ** 

    public final static String calcMD1(String ss) { // MD1 加密算法
       String s = ss == null ? "" : ss; // 若为null返回空
       char hexDigits[] = { '0','1', '2', '3', '4', '1', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; // 字典
       try {
           byte[] strTemp = s.getBytes();                          // 获取字节
           MessageDigestmdTemp = MessageDigest.getInstance("MD1"); // 获取MD1
           mdTemp.update(strTemp);                                // 更新数据
           byte[] md =mdTemp.digest();                        // 加密
           int j =md.length;                                 // 加密后的长度
           char str[] = new char[j * 2];                       // 新字符串数组
           int k =0;                                         // 计数器k
           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");          // 设置request编码
   response.setCharacterEncoding("UTF-8");        // 设置response编码

   String action =request.getParameter("action"); // 获取action参数

   if("login".equals(action)) {                       // 如果为login动作
        String account =request.getParameter("account"); // 获取account参数
        String password =request.getParameter("password"); // 获取password参数
        int timeout = new Integer(request.getParameter("timeout")); // 获取timeout参数

        String ssid =calcMD1(account + KEY); // 把账号、**使用MD1加密后保存

        Cookie accountCookie = new Cookie("account", account); // 新建Cookie
        accountCookie.setMaxAge(timeout);              // 设置有效期

        Cookie ssidCookie =new Cookie("ssid", ssid);   // 新建Cookie
       ssidCookie.setMaxAge(timeout);                 // 设置有效期

       response.addCookie(accountCookie);             // 输出到客户端
       response.addCookie(ssidCookie);            // 输出到客户端

       // 重新请求本页面,参数中带有时间戳,禁止浏览器缓存页面内容
       response.sendRedirect(request.getRequestURI() + "?" + System.currentTimeMillis());
       return;
    } else if("logout".equals(action)) {                  // 如果为logout动作
       CookieaccountCookie = new Cookie("account", ""); // 新建Cookie,内容为空
       accountCookie.setMaxAge(0); // 设置有效期为0,删除

       Cookie ssidCookie =new Cookie("ssid", ""); // 新建Cookie,内容为空
       ssidCookie.setMaxAge(0);                   // 设置有效期为0,删除
       response.addCookie(accountCookie);         // 输出到客户端
       response.addCookie(ssidCookie);         // 输出到客户端
       // 重新请求本页面,参数中带有时间戳,禁止浏览器缓存页面内容
       response.sendRedirect(request.getRequestURI() + "?" + System.currentTimeMillis());
       return;
    }
    boolean login = false;                        // 是否登录
    String account = null;                        // 账号
    String ssid = null;                           // SSID标识

    if(request.getCookies() !=null) {               // 如果Cookie不为空
        for(Cookie cookie : request.getCookies()) {  // 遍历Cookie
           if(cookie.getName().equals("account"))  // 如果Cookie名为 account
               account = cookie.getValue();       // 保存account内容
           if(cookie.getName().equals("ssid")) // 如果为SSID
               ssid = cookie.getValue();          // 保存SSID内容
        }
    }
    if(account != null && ssid !=null) {    // 如果account、SSID都不为空
        login = ssid.equals(calcMD1(account + KEY)); // 如果加密规则正确, 则视为已经登录
    }
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01Transitional//EN">
       <legend><%= login ? "欢迎您回来" : "请先登录"%></legend>
        <% if(login){%>
            欢迎您, ${cookie.account.value }. &nbsp;&nbsp;
           <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><inputtype="password" name="password"></td>
               </tr>
               <tr>
                   <td>有效期: </td>
                   <td><inputtype="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>
        <% } %>

提示:该加密机制中最重要的部分为算法与**。由于MD1算法的不可逆性,即使用户知道了账号与加密后的字符串,也不可能解密得到**。因此,只要保管好**与算法,该机制就是安全的。

session:
Session是服务器端使用的一种记录客户端状态的机制,使用上比Cookie简单一些,相应的也增加了服务器的存储压力。
在服务器端程序运行的过程中创建的,不同语言实现的应用程序有不同创建Session的方法,而在Java中是通过调用HttpServletRequest的getSession方法(使用true作为参数)创建的。在创建了Session的同时,服务器会为该Session生成唯一的Session id,而这个Session id在随后的请求中会被用来重新获得已经创建的Session;在Session被创建之后,就可以调用Session相关的方法往Session中增加内容了,而这些内容只会保存在服务器中,发到客户端的只有Session id;当客户端再次发送请求的时候,会将这个Session id带上,服务器接受到请求之后就会依据Session id找到相应的Session,从而再次使用之。正式这样一个过程,用户的状态也就得以保持了。
Session对应的类为javax.servlet.http.HttpSession类。每个来访者对应一个Session对象,所有该客户的状态信息都保存在这个Session对象里。Session对象是在客户端第一次请求服务器的时候创建的。Session也是一种key-value的属性对,通过getAttribute(Stringkey)和setAttribute(String key,Objectvalue)方法读写客户状态信息。Servlet里通过request.getSession()方法获取该客户的Session,例如:
Servlet里通过request.getSession()方法获取该客户的Session:

HttpSession session = request.getSession();       // 获取Session对象
session.setAttribute("loginTime", new Date());     // 设置Session中的属性
out.println("登录时间为:" +(Date)session.getAttribute("loginTime"));      // 获取Session属性

JSP中内置了Session隐藏对象,可以直接使用:

...
    if(request.getMethod().equals("POST"))
    { 
        // 如果是POST登录       
        for(Person person :persons)
        {           
           // 遍历基础数据,验证账号、密码
           // 如果用户名正确且密码正确
           if(person.getName().equalsIgnoreCase(request.getParameter("username"))&&person.getPassword().equals(request.getParameter("password")))
           {              
               // 登录成功,设置将用户的信息以及登录时间保存到Session
               session.setAttribute("person", person);                   // 保存登录的Person
               session.setAttribute("loginTime", new Date());          // 保存登录的时间              
               response.sendRedirect(request.getContextPath() + "/welcome.jsp");
               return;
            }
        }      
        message = "用户名密码不匹配,登录失败。";       // 登录失败
    }
...

登录界面验证用户登录信息,如果登录正确,就把用户信息以及登录时间保存进Session,然后转到欢迎页面welcome.jsp。welcome.jsp中从Session中获取信息,并将用户资料显示出来。
welcome.jsp:

<%
    Person person =(Person)session.getAttribute("person");                       // 获取登录的person
    Date loginTime =(Date)session.getAttribute("loginTime");                     // 获取登录时间
%>

注意:程序中Session中直接保存了Person类对象与Date类对象,使用起来要比Cookie方便。当多个客户端执行程序时,服务器会保存多个客户端的Session。获取Session的时候也不需要声明获取谁的Session。Session机制决定了当前客户只会获取到自己的Session,而不会获取到别人的Session。各客户的Session也彼此独立,互不可见。

提示:Session的使用比Cookie方便,但是过多的Session存储在服务器内存中,会对服务器造成压力。为防止内存溢出,服务器会把长时间内没有活跃的Session从内存删除。这个时间就是Session的超时时间。Session的超时时间为maxInactiveInterval属性,可以通过对应的getMaxInactiveInterval()获取,通过setMaxInactiveInterval(longinterval)修改。Session的超时时间也可以在web.xml中修改。另外,通过调用Session的invalidate()方法可以使Session失效。

服务器一般把Session放在内存里。当大量客户访问服务器时可能会导致内存溢出。Session在用户第一次访问服务器的时候自动创建。需要注意只有访问JSP、Servlet等程序时才会创建Session,只访问HTML、IMAGE等静态资源并不会创建Session。如果尚未生成Session,也可以使用request.getSession(true)强制生成Session。

Session生成后,只要用户继续访问,服务器就会更新Session的最后访问时间,并维护该Session。用户每访问服务器一次,无论是否读写Session,服务器都认为该用户的Session“活跃(active)”了一次。
常用方法:

void setAttribute(String attribute, Object value):设置Session属性。value参数可以为任何Java Object。通常为Java Bean。value信息不宜过大 
String getAttribute(String attribute):返回Session属性 
Enumeration getAttributeNames():返回Session中存在的属性名 >
void removeAttribute(String attribute):移除Session属性 String getId():返回Session的ID。该ID由服务器自动创建,不会重复 
long getCreationTime():返回Session的创建日期。返回类型为long,常被转化为Date类型,例如:Date createTime = new Date(session.get >CreationTime()) 
long getLastAccessedTime():返回Session的最后活跃时间。返回类型为long 
int getMaxInactiveInterval():返回Session的超时时间。单位为秒。超过该时间没有访问,服务器认为该Session失效 
void setMaxInactiveInterval(int second):设置Session的>超时时间。单位为秒 void putValue(String attribute, Object value):不推荐的方法。已经被setAttribute(String attribute, Object Value)替代 
void invalidate():使该Session失效

Session需要使用Cookie作为识别标志。HTTP协议是无状态的,Session不能依据HTTP连接来判断是否为同一客户,因此服务器向客户端浏览器发送一个名为JSESSIONID的Cookie,它的值为该Session的id(也就是HttpSession.getId()的返回值)。Session依据该Cookie来识别是否为同一用户。
该Cookie为服务器自动生成的,它的maxAge属性一般为–1,表示仅当前浏览器内有效,并且各浏览器窗口间不共享,关闭浏览器就会失效。
因此同一机器的两个浏览器窗口访问服务器时,会生成两个不同的Session。但是由浏览器窗口内的链接、脚本等打开的新窗口(也就是说不是双击桌面浏览器图标等打开的窗口)除外。这类子窗口会共享父窗口的Cookie,因此会共享一个Session。
注意:新开的浏览器窗口会生成新的Session,但子窗口除外。

URL地址重写。
URL地址重写是对客户端不支持Cookie的解决方案。URL地址重写的原理是将该用户Session的id信息重写到URL地址中。服务器能够解析重写后的URL获取Session的id。
附加方式也有几种:
一种是作为URL路径的附加信息,表现形式为
http://...../xxx;jsessionid= ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
一种是作为查询字符串附加在URL后面,表现形式为
http://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
另一种技术叫做表单隐藏字段。就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把session id传递回服务器。

注意:TOMCAT判断客户端浏览器是否支持Cookie的依据是请求中是否含有Cookie。尽管客户端可能会支持Cookie,但是由于第一次请求时不会携带任何Cookie(因为并无任何Cookie可以携带),URL地址重写后的地址中仍然会带有jsessionid。当第二次访问时服务器已经在浏览器中写入Cookie了,因此URL地址重写后的地址中就不会带有jsessionid了。

session并不安全,依然可被接触:
https://www.freebuf.com/articles/web/131305.html