cookie和session详解java版本
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。使用时一定要注意。
- 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。
- path表示cookie所在的目录,默认为/,就是根目录。在同一个服务器上有目录如下:/test/,/test/cd/,/test/dd/,现设一个cookie1的path为/test/,cookie2的path为/test/cd/,那么test下的所有页面都可以访问到cookie1,而/test/和/test/dd/的子页面不能访问cookie2。这是因为cookie能让其path路径下的页面访问。
- 浏览器会将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 }.
<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