您的位置:首页 > 其它

会话跟踪技术介绍——cookie ,url 重写, 隐藏表单域

2007-06-09 18:54 381 查看
由于HTTP协议是一种无状态的协议,也就是说当用户请求一个资源,服务器端根据请求做出响应,回发到客户端后,就关闭了连接(HTTP无状态协议详细请参考我的另外一篇文章)
通俗易懂客户端与服务器端交互原理(HTTP数据请求与HTTP响应,包括Servlet部分) http://blog.csdn.net/lvpin/archive/2007/06/09/1645770.aspx

但是,我们在实际生活中可能有过网上购物的经历,通过不同的请求购买到了不同的商品,并放在“购物车”中,那么对于服务器来说,它是如何知道车中已经有了些什么物品?同样的,在支付费用时服务器又怎么样来确定以前所建立的购物车是属于哪一位客户的呢?很显然,HTTP连接是一个请求,一个响应,独立的连接,无法解决。这个时候就出现了3种类型的方法来满足这一实际需要——cookie ,url 重写, 隐藏表单域。

1.Cookie 实现会话跟踪机制。

通俗的说,就是每当客户端请求一次,服务器端就响应一次,并且客户端将接受到的结果保存在本地机上,一个保存了用户信息的文件,称谓Cookie 。此后,当客户端再次对同一个服务器作出请求时,首先,服务器端会检索Cookie 文件信息,判断是否为同一用户,先前有哪些信息等等。再作出响应,并把这些响应的结果再次写到客户端的 Cookie 中去,替换或者追加一些内容。如此循环,请求和响应,服务器就可以根据Cookie知道各个请求是否来自于同一客户,且包括此前保存的有用的信息。请求-响应-写入Cookie-断掉连接 再次请求-读取Cookie-响应-写入Cookie-断掉连接。

最明显的例子就是 我们在登录我们的邮箱,或者一个网站(如CSDN)时,就可以把用户名和密码保存在我们本地机上的Cookie。下次当再次打开这个网站时,会发现我们根本就没有输入姓名和密码就直接进入了以前要去的一个版快。这个实际上就是Cookie的实际运用,当我们请求这个网站时(服务器),服务器读取了Cookie的信息,来判断这个用户是否以前也登陆过这个网站,直接读取用户名和密码代替用户完成了登陆动作。

再举一个例子,第一张页面是登陆框,要求输入用户ID号和密码,第二张页面是选择购物同时支付费用。那么服务器如何判断这些物品是哪一位用户的?因为登陆和提交购物支付是两种不同的请求,不会保存一个持续的连接的状态。如果用 Cookie 的话,就把第一次用户的ID,密码等保存到一个有标识的Cookie中。第二次请求要求支付以选择好的物品时,这个时候再次读取Cookie 得到用户ID号,根据这个ID号,在此用户的帐户上扣除购买物品的费用。这样就实现了 能够把用户ID “保存”在不同的页面请求中了。

2.URL重写

但是有一种情况就是客户端禁止使用Cookie的话,该如何实现会话跟踪呢?这个时候最简单的就是 URL重写,以参数的形式附加到要请求页面的URL后面。当第二张页面提交时,会同时解析此URL ,得到所需要的参数。

比如一个页面我们可以这样来操作:http://blog.csdn.net/ default.aspx ? name=simonlv&password=11111

那么我们在第二个页面就能得到 参数 name 的值 simonlv 参数 password的值111 ,那么下次提交的时候,这几个值就确保了服务器端仍然能够知道是哪一个用户。

但是这样有一个缺点,就是 暴露了一些敏感的信息。那么在我们的 response对象中有一个 encodeURL的方法,通过这个方法能把 http://blog.csdn.net/ default.aspx ? name=simonlv&password=11111 变成为

http://blog.csdn.net/simonlv/ 或者 http://blog.csdn.net/default.aspx;name=%$#%@$& password=$#@$#@

类似于这样的形式,这样就实现了参数在不同页面间的传递,并且隐藏了部分敏感的信息。

encodelURl 做了些什么动作,这个我会在以后的文章中详细介绍,它其实就是根据 正则表达式 来解析http://blog.csdn.net/simonlv/ 类似于这样的格式,如果匹配 就转向 http://blog.csdn.net/default?name=simonlv

这样的内部资源,这个内部资源估计是看不到的。

这样的一种形式就是重写了URL ,实现了参数的传递。

在网上找了两行代码,可以看看 encodeURL的用法:

hello1.jsp

<%@ page session="true" %>
<%
Integer num = new Integer(100);
session.putValue("num",num);
String url =response.encodeURL("hello2.jsp"); -----------重写了URL ,将参数附加到了此URL后。
%>
<a href='<%=url%>'>hello2.jsp</a>

hello2.jsp

<%@ page session="true" %>
<%
Integer i= (Integer)session.getValue("num");
out.println("Num value in session is "+i.intValue());
%>

关于URL 重写,我会在后面的文章继续聊到,自己也可以写一个 encodeURL( )的方法玩玩吧。

3.隐藏表单域

是一种最简单的方式,将字段隐藏在HTML表单中,但不在客户端显示。比如在第一张页面中输入用户名和密码登陆,服务器生成响应返回第二张页面。当第二张页面提交时可能仍然需要知道来自第一张页面中的用户名。

那么就可以通过隐藏表单域来实现这一连续的过程。当第一张页面提交后,服务器端作出响应返回第二张页面,此页面中用隐藏域记录了来自登陆时的用户名。通俗说就是当服务器回发给客户端的响应中,就同时把用户名再次回发到客户端,用隐藏域隐藏起来,是不可见的。当第二张页面提交时,此隐藏域中的用户名一并随表单提交。这样服务器就仍然可以判断此用户是否与以前的用户相同。于是,再次处理完结果后继续将响应回发给客户端,且此响应中也仍然包含了用户名,在客户端中仍然用隐藏域将这一信息隐藏。

这样就完成了一个连续请求的动作,但是对于用户,这是不可见的。

以下这个例子就是一个购物车的例子

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;

public class SessionServlet
extends HttpServlet {
private static final String CONTENT_TYPE = "text/html; charset=GBK";

public void doGet(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {

response.setContentType(CONTENT_TYPE);
PrintWriter out = response.getWriter();

out.println("<html><head><title>当前购物车中的商品</title></head>");
out.println("<body>");

request.setCharacterEncoding("GBK");

String[] items = request.getParameterValues("item"); // 第一次请求时肯定是没有参数的

out.println("当前,在你的购物车中有下列商品:");

if (items == null) {
out.println("没有商品!"); // 所以第一次是没有商品的
}
else {
out.println("<UL>");
for (int i = 0; i < items.length; i++) { // 如果有商品就罗列出来 html 中的 ul 和 li 标签
out.println("<LI>");
out.println(items[i]);
}
out.println("</UL>");
}

// 下次请求时 action 与 method ,表单提交

out.println("<form action=/SessionModule/sessionservlet method=post>");

if (items != null) {
for (int i = 0; i < items.length; i++) {
System.out.println(
"把从服务器端回发给客户端的商品内容重新填写在文本框中,以便下一次提交时能把所以数据一起提交到服务器端!");
out.println("<input type=/"hidden/" name=item value=/"" + items[i] +
"/">"); // 如果把hidden 改为text ,那么你就能看到服务器端发送给客户端的是一个隐藏了的商品信息
}
}
System.out.println("随机数,模拟添加一个商品!");
int i = new Random().nextInt(100);
out.println("<input type=/"hidden/" name =/"item/" value=/"商品:");
out.println(String.valueOf(i));
out.println("/"/>");

out.println("<br>你愿意");
out.println("<input type=/"submit/" value=/"添加商品/" />");

// submit 提交的时候,请求的自然是post 方法,处理这个表单自然就是上面红线部分的 sessionservlet

out.println("</form>");
out.println("</body>");
out.println("</html>");
out.close();
}

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

使用比较频繁的还是利用 Servlet 中的 session 来实现 会话跟踪,在这里就先不写了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: