会话跟踪常用技术Session,Cookie:
Http协议是一种无状态的协议,一旦客户端与服务端数据交换完成后则会断开连接,再次交换数据的时候需要重新进行连接,因而服务器是无法从连接上跟踪会话的。
Cookie技术:
由W3C组织提出的,现在已成为一种标准,主流的浏览器都支持Cookie
Cookie实际上就是一小段的文本信息,客户端请求服务端,如果服务端要记住用户的状态则会通过response向客户端发送一个Cookie.浏览器会把这个Cookie保存起来,当浏览器再次请求这个网站的时候会把请求的网址连同Cookie一起交给服务器,服务器这个时候会检查Cookie来辨认用户状态。服务器也可以根据需要修改Cookie的内容
要查看一个网站颁发的Cookie可以在IE浏览器地址栏中输入:
javascript:alert(document.cookie)即可
这个时候会弹出一个对话框显示网站的Cookie
注意:Cookie是需要浏览器支持的,如果浏览器不支持Cookie的话,Cookie功能就会失效
Java中把Cookie封装成了javax.servlet.http.Cookie类,每一个Cookie都是这个Cookie类的对象服务器通过操作Cookie类对象对客户端Cookie进行操作,通过request.getCookie()获取客户端提交的所有Cookie(以Cookie[]的形式返回),通过response.addCookie(Cookie cookie)向客户端设置Cookie
Cookie对象使用key-value属性对的形式保存用户状态
JSP中使用Cookie不需要import它,因为它是javax.servlet.http.*下面
关于方法:Cookie[] cookies = request.getCookies();它是取的浏览器中保存的cookie因而与服务端无关,这表明不同的浏览器(客户端)取到的是自己的cookie与其它访问这个服务端无关
Cookie的不可跨域名性:
也就是说,不同的网站在访问的时候只会携带这个网站的cookie,而且只能修改这个网站的cookie,不会出现其它的网站改了别人网站的cookie的情况
有一点要特别注意的就是:
Cookie中只能保存ASCII的文本因而
1, 如果要保存中文和话则需要如下:
Cookie cookie = new Cookie(“username”,URLEncoder.encode(cookieValue, "UTF-8"));
2, 在取出的时候要如下
String value = URLDecoder.decode(cookie.getValue(),”UTF-8”);
关于Unicode编码问题:
中文属于Unicode字符在内存中占4个字节英文占2个字节属于ASCII码,Cookie在使用Unicode字符的时候需要对字符进行编码否则会出现乱码的情况
编码:java.net.URLEncoder类的encode(String str,String encoding);
解码:java.net.URLDecoder 类的 decode(String str,String encoding);
例如:
//创建一个名为username的cookie
String cookieValue = URLEncoder.encode(request.getParameter("username"),"utf-8");
Cookie usernameCookie = new Cookie("username",cookieValue);
//取值的时候
String username = cookie.getValue();
String decodeName = URLDecoder.decode(username, "utf-8")
比如如下:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ page import="java.net.URLEncoder,java.net.URLDecoder" %>
<%
//关于中文Cookie
Cookie cookie = new Cookie(URLEncoder.encode("姓名","UTF-8"),URLEncoder.encode("谢声","UTF-8"));
//把Cookie保存到客户端
response.addCookie(cookie);
%>
</head>
<body>
<%
if(request.getCookies()!=null){
//如果存在cookie,遍历它
for(Cookie ck:request.getCookies()){
String cookieName = URLDecoder.decode(ck.getName(),"UTF-8");
String cookieValue = URLDecoder.decode(ck.getValue(),"UTF-8");
//把cookie的信息输出
out.println(cookieName+"="+cookieValue);
}
}
else{
out.println("Cookie已写到客户端,请刷新页面!");
}
%>
</body>
</html>
BASE64编码:
Cookie中还可以使用二进制的编码,如果要保存二进制的编码的话则要使用BASE64进行编码
但是对于Cookie中存放二进制的数据是不实用的,因为对于Cookie中的内容应该是少而精的因为每次进行访问的时候都要向服务器发送数据。
Cookie常用属性:
String name:这个是Cookie的名称,Cookie一旦创建后则是不可以更改的
Object value:这个是Cookie的值,
如果值是Unicode字符,则需要为字符进行编码
编码:java.net.URLEncoder类的encode(String str,String encoding/*UTF-8*/);
解码:java.net.URLDecoder 类的 decode(String str,String encoding/*UTF-8*/);
如果是二进制的数据则要使用BASE64进行编码
int maxAge:Cookie失效时间,单位是秒,如果是正数则在多少秒后这个Cookie会失效,如果是负数的话则Cookie是一个临时的Cookie会在关闭浏览器的时候失效
boolean secure:指明这个cookie仅被使用安全协议传输,对于安全协议一般有HTTPS,SSL,这样在网络上进行传输的时候会先进行加密
String path:这个是设置Cookie使用的路径,如果设置为“/session/”则只有contextPath为“/session”的程序可以访问Cookie,如果设为”/”则本域名都可以访问Cookie
String domain:设置可以访问这个Cookie的域名,比如“.baidu.com”则只有baidu.com结束的域名可以访问这个Cookie,第一个字符必须是.
String comment:对这个Cookie的用处的说明,浏览器在显示Cookie信息的时候显示这个说明
int version:Cookie的版本说明,0表示遵循Netscape的规范,1表示遵循W3C的规范
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ page import="java.net.URLEncoder,java.net.URLDecoder" %>
<%!
/**
定义一个全局方法,判断字符串是否为空
*/
boolean isNull(String str){
return str ==null || str.trim().length() == 0;
}
/**
定义一个全局方法,判断字符串是否是数字
*/
boolean isNumber(String str){
boolean isNan = false;
if(!isNull(str)){
//判断是否是一个数字有如下方法
//法一:用字符串new出一个Long类型如果有异常则不是一个数字,如果没有异常则是一个数字
//try{
// Long testlong = new Long(str);
// isNan = true;
// }catch(Exception e){
// isNan = false;
// }
//法二:使用正则来进行判断
String reg = "\\d+\\";
isNan = str.matches(reg);
}
return isNan;
}
%>
<%
request.setCharacterEncoding("UTF-8");
if("POST".equals(request.getMethod())){
//如果是POST请求
String name = request.getParameter("name"); //获得name的请求参数值
String value = request.getParameter("value"); //获得value的请求参数值
String maxAge = request.getParameter("maxAge"); //获得maxAge的请求参数值
String domain = request.getParameter("domain"); //获得domain的请求参数值
String path = request.getParameter("path"); //获得path的请求参数的值
String comment = request.getParameter("comment"); //获得comment请求参数的值
String secure = request.getParameter("secure"); //获得secure请求参数的值
if(!isNull(name)){
//如果name参数值不是空的则创建一个Cookie
Cookie cookie = new Cookie(URLEncoder.encode(name,"UTF-8"),
URLEncoder.encode(value,"UTF-8"));
//如果maxAge非空,并且是数字字符串则设置maxAge属性
if(!isNull(maxAge)&&isNumber(maxAge))
cookie.setMaxAge(Integer.parseInt(maxAge));
else
cookie.setMaxAge(300); //如果没有输入最大时效,默认是5分钟
if(!isNull(domain)) cookie.setDomain(domain);
if(!isNull(path)) cookie.setPath(path);
if(!isNull(comment)) cookie.setComment(comment);
if(!isNull(secure)) cookie.setSecure("true".equalsIgnoreCase(secure));
response.addCookie(cookie); //把经过设置过的cookie写到客户端覆盖原来的cookie
}
}
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<link rel="stylesheet" type="text/css" href="css/style.css">
<title>Cookie 设置</title>
</head>
<body>
<div align="center" style="margin:10px;">
<fieldset>
<legend>当前有效的Cookie</legend>
<!-- <script type="text/javascript">
document.write(document.cookie);
</script> -->
<%
Cookie[] cks = request.getCookies();
for(Cookie ck:cks){
if(!"JSESSIONID".equals(ck.getName()))
out.println(URLDecoder.decode(ck.getName(),"UTF-8")+
" = "+URLDecoder.decode(ck.getValue(),"UTF-8"));
}
%>
</fieldset>
<fieldset>
<legend>设置新Cookie</legend>
<form action="setCookie.jsp" method="post">
<table>
<tr>
<td>
名字:
</td>
<td>
<input name="name" type="text" style="width:200px;">
</td>
</tr>
<tr>
<td>
值:
</td>
<td>
<input name="value" type="text" style="width:200px;">
</td>
</tr>
<tr>
<td>
最大时效:
</td>
<td>
<input name="maxAge" type="text" style="width:200px;">
</td>
</tr>
<tr>
<td>
有效域名:
</td>
<td>
<input name="domain" type="text" style="width:200px;">
</td>
</tr>
<tr>
<td>
有效路径:
</td>
<td>
<input name="path" type="text" style="width:200px;">
</td>
</tr>
<tr>
<td>
Cookie说明:
</td>
<td>
<input name="comment" type="text" style="width:200px;">
</td>
</tr>
<tr>
<td>
是否安全性(true/false):
</td>
<td>
<input name="secure" type="text" style="width:200px;">
</td>
</tr>
<tr>
<td>
</td>
<td>
<input type="submit" value=" 提交 " class="button">
<input type="button" value=" 刷新 " class="button" onclick="location='setCookie.jsp'">
</td>
</tr>
</table>
</form>
</fieldset>
</div>
</body>
</html>
Cookie有效期问题:
Cookie的maxAge决定着Cookie的有效期,它的单位是秒
Cookie中通过方法getMaxAge()与setMaxAge(int maxAge)方法来读写maxAge属性
如果maxAge属性为正数则会在maxAge秒之后自动失效,浏览器会把maxAge为正数的Cookie持久化写到Cookie文件中,无论是关了浏览器还是怎么样,只要在maxAge秒之前登录网站Cookie仍然是有效的
下面的代码会使Cookie永远都有效
Cookie ck = new Cookie(“username”,”advnet86”);
ck.setMaxAge(Integer.MAX_VALUE);
response.addCookie(ck);
如果maxAge是负数,表示Cookie仅在本浏览器窗口及本窗口的子窗口中有效,关闭窗口后Cookie就会失效,这样的Cookie称为临时Cookie不会写到Cookie文件中,它是保存在浏览器的内存当中,因而在关闭浏览器后Cookie就消失了,maxAge的默认值是-1
如果maxAge为0,则表示删除Cookie,Cookie机制没有提供删除Cookie的方法,因而可以把这个maxAge设为0表示Cookie即时失效来达到删除Cookie的效果。失效的Cookie会被从Cookie文件中或是浏览器的内存中删除
如下达到删除Cookie的效果
Cookie ck = new Cookie(“username”,”advent86”);
ck.setMaxAge(0);
response.addCookie(cookie);//这一句是需要的因为要删除的话要把原来的Cookie覆盖掉
response对象中提供的对Cookie的操作方法只有一个就是add(Cookie ck);而如果要修改Cookie的话则需要创建一个同名的Cookie来覆盖原来的Cookie
关于Cookie的域名问题:
Cookie是不可跨域名的,不同网站的Cookie是不可以相互之间使用的
正常情况下同一个一级域名下的两个二级域名如:www.advnet86.com,images.advent86.com
它们之间的Cookie也是不可以交互使用的,因为这两者之间的域名不是完全相同的,这个时候如果希望两者可以交互使用域名则需要设置域名
Cookie ck = new Cookie(“username”,”advent86”);
ck.setDomain(“.advent86.com”);
ck.setPath(“/”);
ck.setMaxAge(Integer.MAX_VALUE);
response.addCookie(ck);
一定要注意:domain参数必须是以”.”开始的
domain决定了运行访问Cookie的域名,而path则决定了允许访问Cookie的路径(ContextPath)比如只允许/session/这个下的程序使用Cookie则可以如下:
Cookie ck = new Cookie(“username”,”advent86”);
ck.setPath(“/session/”);
response.addCookie(ck);
当设置路径时设为”/”表示所有路径下都可以使用Cookie,对于path属性需要使用符号”/”结束
Cookie安全属性:
HTTP协议不仅是无状态的,并且是不安全的,使用HTTP协议的数据不会加密而直接在网络上进行传播,这种数据就可能被截获,如果不希望Cookie在HTTP等非安全协议中传输则可以设置Cookie的secure的属性为true,这样浏览器就只会在安全协议中传输此类Cookie
注意:把Cookie的secure属性设置为true也不能完全的安全因为数据还是未加密的要确保安全的话则要对数据进行加密传输
Javascript操作Cookie:
由于Cookie是保存在浏览器端的,因而Javascript是可以操作Cookie的
比如:<script>document.write(document.cookie);</script> 各个cookie之间使用;进行分隔
Javascript中没有专门处理Cookie的API如果想单独获取获取某个Cookie值,只能手工写代码来解析字符串
<script type="text/javascript">
//选定义一个方法返回名为name的Cookie
function getCookie(name){
var str = document.cookie; //获得Cookie字符串
if(!str || str.indexOf(name + "=")<0) //寻找name=
return;
var cookies = str.split("; "); //使用;把所有的Cookie分开
//遍历每一个Cookie
for(var i=0;i<cookies.length;i++){
var cookie = cookies[i];
if(cookie.indexOf(name + "=")==0){
//如果名字为name,取它的value值
var value = cookie.substring(name.length+1);
return decodeURI(value); //把value确码,并返回
}
}
}
//定义一个方法来设置Cookie
function setCookie(name,value){
document.cookie = name + "=" +encodeURI(value);
}
使用Cookie实现自动登录
把相关的登录信息放在Cookie文件当中,并控制好Cookie的有效期,下次再访问的时候就直接验证Cookie中的登录信息就可以了
登录信息一般来说如果有密码之类的还是很危险的,一般是只存一些无关紧要的登录信息或是把重要的信息加密保存在Cookie当中或是放在数据库当中,下次去取数据库中的信息即可,但是这种方式是每次都要去数据库中去取数据
一般还有一种解决方案如果要保存登录信息可以把密码进行md5加密
MD5加密过程:
publicfinalstatic 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();
//获取MD5
MessageDigest mdTemp = MessageDigest.getInstance("MD5");
//更新数据
mdTemp.update(strTemp);
//完成加密
byte[] md = mdTemp.digest();
//获得加密后的字节数组长度
int j = md.length;
//声明一个新的字符数组
char str[] = newchar[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];
}
returnnew String(str);
}catch(Exception e){returnnull;}
}
Session:
Session是服务端使用的一种记录客户端状态的机制,在使用上比Cookie相对简单,但这样会增加服务器的存储压力
服务器把客户端的信息以某种形式记录在服务器上。客户端浏览器再次访问的时候只需要从这个Session中查找该客户的状态就可以了
Session对应的类是javax.servlet.http.HttpSession类
每一位来访者都会对应有一个Session对象,所有客户的状态会保存在这个Session对象当中。
Session的创建时机:当客户端第一次请求服务器的时候创建,它也是一种key-value的属性对的形式
读写方法:getAttribute(String name); setAttribute(String key,Object value);
Servlet中获得客户的Session:request.getSession()
HttpSession session = request.getSession(); //获得Session
session.setAttribute(“longinTime”,new Date());//设置Session中的属性值
//获得session的属性
out.println(“登录时间:”+(Date)session.getAttribute(“loginTime”));
request可以使用getSession(boolean create)获得Session:
request.getSession(false):对于这个方法如果Session不存在则返回null
request.getSession(true)与request.getSession()一样的,如果不存在Session则会创建一个并返回
Servlet中必须使用编程的方式获得HttpSession对象,而在JSP中内置了Session,但注意如果声明了<%@ page session=”false”%>,则在JSP中Session对象是不可用的
对于Session来说我们的用户一般是,定义一个POJO对象
客户请求服务端这时进行相应的处理,这时把相关状态的POJO对象保存在Session当中并跳转到另外的处理处或是页面
从另一个处理处或是页面中取出Session中保存的属性,进行进一步的处理或是显示
注意:对于Session来说各个客户有自己的Session,它们之间是彼此独立的,不会相互的影响
Session的生命周期:
服务器一般会把Session放在内存当中,而每个客户都有一个独立的Session如果Session的内容比较复杂的话则会在大量用户访问的时候产生内存溢出的可能。因而Session应该是尽量的精简
Session在用户第一次访问服务器的时候自动创建,但是要注意只有访问servlet,jsp等程序时才会创建,而对于访问静态资源是不会产生Session,如果没有生成Session可以使用request.getSession(true)/request.getSession()强制生成Session
当Session有了之后只要用户继续访问的话则服务器会更新Session的最后访问时间并维护这个Session
session有效期:
服务器会把在一段时间都没有活跃过的session从内存中删除,这个时间就是Session的有效期
Session的超时时间为maxInactiveInterval属性,可以通过对应的getMaxInactiveInterval()获得
通过setMaxInactiveInterval(long interval)修改
对于Session超时时间也可以在web.xml当中进行修改,通过调用session的invalidate()方法可以使用Session失效
Session常用方法:
void setAttribute(String attribute,Object value);设置session属性,value可以是任意的Java Object通常是Java Bean
Object getAttribute(String attribute);获得Session属性
Enumeration getAttributeNames();获得Session中存在的属性名
void removeAttribute(String attribute);从Session中移除相关属性
String getId();返回Session的id,这个是由服务器自动创建的
long getCreationTime();返回Session的创建日期,返回的类型是long,通常把这个long转成一个Date类型 Date createTime = new Date(session.getCreationTime());
long getLastAccessedTime();返回Session的最后活跃时间
int getMaxInactiveInterval();返回Session的超时时间,单位是秒
void setMaxInactiveInterval(int second);设置Session的超时时间,单位是秒
boolean isNew();返回该Session是否是新创建的
void invalidate();使用Session失效
Tomcat中Session的默认超时时间是20分钟,可以通过void setMaxInactiveInterval(int second);这个进行设置
还可以改变web.xml来修改Session的默认超时时间,注意在web.xml中设置的数值单位人分钟
<session-config>
<session-timeout>60</session-timeout>
</session-config>
Session虽然是保存在服务器的,但是它的正常运行还是需要浏览器的支持,这个是因为Session需要使用Cookie作为识别标志,因为Session要判断是否是同一客户,因而服务器会向客户端浏览器发关一个JSESSIONID的Cookie它的值为这个Session的id,服务器就是跟据这个id来识别是否为同一用户的
关于JSESSIONID的Cookie:
它是服务器自动产生的它的maxAge的值一般是-1,表示它只在当前浏览器中有效,并且各个浏览器之前是不会共享的,关闭浏览器会使用这个Cookie失效,服务器判断是否同一客户就是根据浏览器窗口来的,同一机器不同的浏览器窗口访问服务器的时候会产生两个不同的Session,但要注意的就是由一个窗口产生的子窗口是会共享一个cookie的因而也会共享一个Session
浏览器是可以禁用Cookie的:
如果浏览器禁用掉了Cookie则处理办法为:URL重写
URL地址重写的原理:
把用户Session的id信息重写到URL地址中,服务器会解析重写后的URL获得session的id,这样的话就算是客户端禁用了Cookie还是可以使用Session
HttpServletReaponse类中提供方法:encodeURL(String url);实现URL地址重写
对于这个方法它会去判断客户端是否禁用了Cookie如果没有禁用就用原来的url去访问如果是禁用了Cookie则会生成一个session的id重写到url中去,这个时候当点击链接的时候会把这个Session的id通过URL提交到服务端
如果是重定向的话:
response.encodeRedirectURL(String url);
由于有些客户端会禁用Cookie有些又不会禁用,那么为了统一编程则可以直接把Cookie禁用掉然后使用URL地址重写
通过配置禁用Cookie
META-INF文件夹下有(或是创建)context.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- 把Cookies禁用掉 -->
<context path="/SessionCookie" cookies="false">
</context>
或者是可以在Tomcat全局的conf/context.xml中写上
<context path="/SessionCookie" cookies="false">
…
</context>
注意:上面的配置是禁止了Session使用Cookie作为识别标志,但并不会阻止其它Cookie的读写
Cookie与Session的比较:
1, 存取的方式:
Cookie只能保存ASCII字符串如果需要存取Unicode字符则需可使用URLEncoder.encode(String value,”UTF-8”);//编码
URLDecoder.decode(String value,”UTF-8”);//确码
如果要存取二进制数据则要使用BASE64进行编码(不常用)
Session可以存取任何类型的数据,它可以直接保存Java Bean或是任何Java类
2, 从安全上来看:
Session保存在服务端对用户来说是透明的相对来说是更加的安全,如果要把相关的信息保存在Cookie当中一定要把敏感信息进行加密
3, 从有效期来看:
Cookie保存在客户端可以长久的记录相应的信息只在把Cookie中的maxAge的值设置大一些就可以了,而Session则做不到这一点因为Session依赖于JESSIONID的Cookie这个Cookie的maxAge的默认值是-1,只在关闭了浏览器这个sessionId就会失效,Session也不适于保存时间过长因为把Session长时间放在服务器的内存中时间过行,Session过多就有可能导致内存溢出。
4, 从服务器的负担上来说:
对于并发量大的网站是不太或能使用Session来跟踪客户会话的因为对服务器的压力过大。
5, 从浏览器的支持上来看:
Cookie需要客户端浏览器的支持,如果客户端禁用了Cookie,则Cookie就没用了,但是对于Session来说可以使用URL地址重写来实现
response.encodeURL(String url);
response.encodeRedirectURL(String url);
6, 从跨域名来说:
Cookie支持跨域名来访问,如果把domain属性设置为”.advent86.com”,那么以.advent86.com结尾的域名就都可以访问这个Cookie,而对于Session来说它只对于他所在的域名(应用)内有效。