前言
一直想写一篇关于cookie和session的博客,由于种种原因,一直没有整理,这不,今天还就遇到问题了,之前虽然会,但是好久没用又给忘了,结果还得查资料。是时候填坑了,闲话少说,开干。
application
Application用于保存所有用户的公共的数据信息,在这只是提一下,不过多解释。
下面我主要解说cookie和session,不过在解说之前有必要让大家先了解一下HTTP协议和会话跟踪。
HTTP协议
协议是指计算机通信网络中两台计算机之间进行通信所必须共同遵守的规定或规则
HTTP协议工作于客户端-服务端架构上。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。再通俗点讲,Http协议基本是有一个request/response模型也就是请求/响应模型的,通俗讲也就是“一问一答”的模式,浏览器向服务器发起request请求,这就是“问”;服务器收到请求后,返回response响应,这就是“答”。
需要强调的是HTTP是无连接并且是无状态的
无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
无状态:无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。
HTTP协议是无状态的协议。一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接。这就意味着服务器无法从连接上跟踪会话。
会话跟踪
在计算机术语中,会话是指一个终端用户与交互系统进行通讯的过程,比如从输入账户密码进入操作系统到退出操作系统就是一个会话过程。会话较多用于网络上,TCP的三次握手就创建了一个会话,TCP关闭连接就是关闭会话。
可能这些官方的话有些新手朋友看了不理解,没关系,我再通俗点,假如你就是用户,你的电脑就是服务器,此时你想访问我的博客园,我的博客园网址是https://www.cnblogs.com/zyx110/,然后你把我的博客园地址复制粘贴到了浏览器页面的地址栏里,敲一下回车键,从你复制粘贴网址到敲击回车键这个过程就是客户端向服务器发送一个请求(request),等你敲击过回车键后,你发现你的浏览器页面跳转到了泰斗贤若如的博客园,这个过程就是服务器给客户端的一个响应(response),再联想上面说到的HTTP的无连接和无状态,客户端和服务器之间只有一次联系,而且“说了下句忘了上句”,还有就是这只是客户端和服务器端间的联系,浏览器的请求(request)与请求之间是没有关系的。但是我们想要开发Web应用,就应该让这些请求之间有关系,这就需要我们在多个request请求之间创建一些联系,这就是会话(session),会话简单点讲就是“要想顺利交谈,需要说了下句想起来上句”,多次HTTP连接间维护用户与同一用户发出的不同请求之间关联的情况称为维护一个会话,所以这些建立联系的request请求是属于某个(会话)session的。
再举个例子吧,可能有些朋友看了上面这些还有点犹豫,不要紧,慢慢理解,理论上,一个用户的所有请求操作都应该属于同一个会话,而另一个用户的所有请求操作则应该属于另一个会话,二者不能混淆。例如,你是用户,你在淘宝购买的任何商品都应该放在你的购物车内,不管你是什么时间购买的,这都是属于同一个会话的,不能放入用户B或用户C的购物车内,这不属于同一个会话。而Web应用程序是使用HTTP协议传输数据的。HTTP协议是无连接无状态的协议。一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接。这就意味着服务器无法从连接上跟踪会话。即用户A购买了一件商品放入购物车内,当再次购买商品时服务器已经无法判断该购买行为是属于用户A的会话还是用户B的会话了。要跟踪该会话,必须引入一种机制。
Cookie就是这样的一种机制。它可以弥补HTTP协议无状态的不足。在Session出现之前,基本上所有的网站都采用Cookie来跟踪会话。
会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份,Session通过*在服务器端记录信息确定用户身份*。
cookie
由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份。用户A购买了一件商品放入购物车内,当再次购买商品时服务器已经无法判断该购买行为是属于用户A的会话还是用户B的会话了。怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie 的工作原理。
Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie(通行证)。客户端会把Cookie保存起来。
当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。
我们访问浏览器的时候,浏览器会发送一个HTTP请求到服务器端;
服务器会发送一个HTTP响应到客户端,其中包括Sst-Cookie,意思就是浏览器建立一个cookie保存服务器指定的内容,比如用户信息和用户操作信息;
浏览器保存好信息之后,下次我们再次访问网站的时候,浏览器再发送HTTP请求到服务器端时都会携带之前保存的cookie;
服务器端会从收到的cookie中识别用户身份,就能让页面为你提供专门属于你的内容了。
比如我们从网站的登陆界面中看到有记住用户名这个选项,你勾选了它以后,登录成功,浏览器就会把你的信息放在cookie里,下次再访问这个网站的时候,服务器就能根据收到的cookie识别出是你,帮你自动登陆,显示专属于你的内容。
session
Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录
在服务器上。这就是Session。客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。
每个用户访问服务器都会建立一个session,那服务器是怎么标识用户的唯一身份呢?事实上,用户与服务器建立连接的同时,服务器会自动为其分配一个SessionId。
思考两个问题:
什么东西可以让你每次请求都把SessionId自动带到服务器呢?
显然就是cookie了,如果你想为用户建立一次会话,可以在用户授权成功时给他一个唯一的cookie。当一个用户提交了表单时,浏览器会将用户的SessionId自动附加在HTTP头信息中,(这是浏览器的自动功能,用户不会察觉到),当服务器处理完这个表单后,将结果返回给SessionId
所对应的用户。试想,如果没有 SessionId,当有两个用户同时进行注册时,服务器怎样才能知道到底是哪个用户提交了哪个表单呢。
web开发发展至今,cookie和session的使用已经出现了一些非常成熟的方案。在如今的市场或者企业里,一般有两种存储方式:
- 存储在服务器端:通过cookie存储一个sessionid,然后具体的数据则是保存在session中。如果用户已经登录,则服务器会在cookie中保存一个sessionid,下次再次请求的时候,会把该sessionid携带上来,服务器根据sessionid在session库中获取用户的session数据。就能知道该用户到底是谁,以及之前保存的一些状态信息。这种专业术语叫做server side session。
- 将session数据加密,然后存储在cookie中。这种专业术语叫做client side session。flask采用的就是这种方式,但是也可以替换成其他形式。
- 如何储存需要的信息?
服务器通过SessionId作为key,读写到对应的value,这就达到了保持会话信息的目的。
session的创建:
当程序需要为某个客户端的请求创建一个session时,服务器首先检查这个客户端的请求里是否已包含了sessionId,如果已包含则说明以前已经为此客户端创建过session,服务
器就按照sessionId把这个session检索出来使用(检索不到,会新建一个),如果客户端请求不包含sessionId,则为此客户端创建一个session并且生成一个与此session相关
联的sessionId,sessionId的值是一个既不会重复,又不容易被找到规律以仿造的字符串,这个sessionId将被在本次响应中返回给客户端保存。
禁用cookie:
如果客户端禁用了cookie,通常有两种方法实现session而不依赖cookie。
- URL重写,就是把sessionId直接附加在URL路径的后面。
- 表单隐藏字段。就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把session id传递回服务器。比如:
Session共享:
对于多网站(同一父域不同子域)单服务器,我们需要解决的就是来自不同网站之间SessionId的共享。由于域名不同(aaa.test.com和bbb.test.com),而SessionId又分别储存
在各自的cookie中,因此服务器会认为对于两个子站的访问,是来自不同的会话。解决的方法是通过修改cookies的域名为父域名达到cookie共享的目的,从而实现SessionId的共
享。带来的弊端就是,子站间的cookie信息也同时被共享了。
cookie和session案例解析(记住我)
- package servlet;
- import domain.User;
- import javax.servlet.ServletException;
- import javax.servlet.annotation.WebServlet;
- import javax.servlet.http.Cookie;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
- import java.util.List;
- /*
- * 用户登录的Servlet
- * */
- @WebServlet("/login")
- public class LoginServlet extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- req.setCharacterEncoding("utf-8");
- resp.setCharacterEncoding("utf-8");
- //接收数据
- String username = req.getParameter("username");
- String password = req.getParameter("password");
- //从ServletContext域中获得保存用户信息集合
- List<User> list = (List<User>) this.getServletContext().getAttribute("list");
- for (User user:list){
- //判断用户名是否正确
- if (username.equals(user.getUsername())){
- //判断密码是否正确
- if (password.equals(user.getPassword())){
- //用户名密码都正确
- //登录成功
- //判断记住用户名复选框是否勾选
- String remember = req.getParameter("remember");
- if ("true".equals(remember)){
- //完成记住用户名的功能
- Cookie cookie = new Cookie("username",username);
- //设置有效路径
- cookie.setPath("/login.jsp");//设置此路径后只能在login.jsp访问cookie
- //设置有效时间
- cookie.setMaxAge(60*60*24);
- //将cookie回写到浏览器
- resp.addCookie(cookie);
- }
- //将用户的信息保存到Session中
- req.getSession().setAttribute("user",user);
- resp.sendRedirect("/success.jsp");
- return;
- }
- }
- }
- //登录失败
- req.setAttribute("msg","用户名或密码错误!");
- req.getRequestDispatcher("/login.jsp").forward(req,resp);
- }
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- doGet(req, resp);
- }
- }
- package utils;
- import javax.servlet.http.Cookie;
- public class CookieUtils {
- public static Cookie findCookie(Cookie[] cookies,String name){
- if (cookies==null){
- //说明客户端没有携带Cookie
- return null;
- }else {
- //说明客户端携带Cookie
- for (Cookie cookie:cookies){
- if (name.equals(cookie.getName())){
- return cookie;
- }
- }
- return null;
- }
- }
- }
- <%@page import="utils.CookieUtils"%>
- <%@ page language="java" contentType="text/html; charset=UTF-8"
- pageEncoding="UTF-8"%>
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="UTF-8">
- <title>登录页面</title>
- <link rel="stylesheet" href="./css/login.css">
- </head>
- <body>
- <div class="login">
- <div class="header">
- <h1>
- <a href="/login.jsp">登录</a> <a href="/regist.jsp">注册</a>
- </h1>
- </div>
- <%
- String username="";
- //获得从客户端携带过来的所有的Cookie
- Cookie[] cookies = request.getCookies();
- // 从Cookie的数组中查找指定名称的Cookie
- Cookie cookie = CookieUtils.findCookie(cookies, "username");
- if(cookie != null){
- username = cookie.getValue();
- }
- if(session.getAttribute("username")!=null){
- username = (String)session.getAttribute("username");
- }
- String msg = "";
- if(request.getAttribute("msg")!=null){
- msg = (String)request.getAttribute("msg");
- }
- %>
- <h3><%=msg %></h3>
- <form action="/login" method="post">
- <table>
- <tr>
- <td class="td1">用户名</td>
- <td><input type="text" class="input1" name="username" value="<%=username %>"></td>
- </tr>
- <tr>
- <td class="td1">密码</td>
- <td><input type="password" class="input1" name="password"></td>
- </tr>
- <tr>
- <td class="td1" colspan="2">
- <input type="checkbox" name="remember" value="true" checked="checked"> 记住用户名</td>
- </tr>
- <tr>
- <td colspan="2">
- <div class="btn-red">
- <input type="submit" value="登录" id="login-btn">
- </div>
- </td>
- </tr>
- </table>
- </form>
- </div>
- </body>
- </html>
Cookie知识拓展
设置Cookie的所有属性
除了name与value之外,Cookie还具有其他几个常用的属性。每个属性对应一个getter方法与一个setter方法。
Cookie常用属性
Cookie的有效期
Cookie的maxAge决定着Cookie的有效期,单位为秒(Second)。Cookie中通过getMaxAge()方法与setMaxAge(int maxAge)方法来读写maxAge属性。
如果maxAge属性为正数,则表示该Cookie会在maxAge秒之后自动失效。浏览器会将maxAge为正数的 Cookie持久化,即写到对应的Cookie文件中。无论客户关闭了浏览器还是电脑,只要还在maxAge秒之前,登录网站时该Cookie仍然有效。下面代码中的Cookie信息将永远有效。
Cookie cookie = new Cookie("username","helloweenvsfei"); // 新建Cookie
cookie.setMaxAge(Integer.MAXVALUE); // 设置生命周期为MAXVALUE
response.addCookie(cookie); // 输出到客户端
如果maxAge为负数,则表示该Cookie仅在本浏览器窗口以及本窗口打开的子窗口内有效,关闭窗口后该 Cookie即失效。maxAge为负数的Cookie,为临时性Cookie,不会被持久化,不会被写到Cookie文件中。Cookie信息保存在浏 览器内存中,因此关闭浏览器该Cookie就消失了。Cookie默认的maxAge值为–1。
如果maxAge为0,则表示删除该Cookie。Cookie机制没有提供删除Cookie的方法,因此通过设置该Cookie即时失效实现删除Cookie的效果。失效的Cookie会被浏览器从Cookie文件或者内存中删除,
例如:
Cookie cookie = new Cookie("username","helloweenvsfei"); // 新建Cookie
cookie.setMaxAge(0); // 设置生命周期为0,不能为负数
response.addCookie(cookie); // 必须执行这一句
response对象提供的Cookie操作方法只有一个添加操作add(Cookie cookie)。
要想修改Cookie只能使用一个同名的Cookie来覆盖原来的Cookie,达到修改的目的。删除时只需要把maxAge修改为0即可。
注意:从客户端读取Cookie时,包括maxAge在内的其他属性都是不可读的,也不会被提交。浏览器提交Cookie时只会提交name与value属性。maxAge属性只被浏览器用来判断Cookie是否过期。
Cookie的修改、删除
Cookie并不提供修改、删除操作。如果要修改某个Cookie,只需要新建一个同名的Cookie,添加到response中覆盖原来的Cookie。
如果要删除某个Cookie,只需要新建一个同名的Cookie,并将maxAge设置为0,并添加到response中覆盖原来的Cookie。注意是0而不是负数。负数代表其他的意义。读者可以通过上例的程序进行验证,设置不同的属性。
注意:修改、删除Cookie时,新建的Cookie除value、maxAge之外的所有属性,例如name、path、domain等,都要与原Cookie完全一样。否则,浏览器将视为两个不同的Cookie不予覆盖,导致修改、删除失败。
Cookie的域名
Cookie是不可跨域名的。域名www.google.com颁发的Cookie不会被提交到域名www.baidu.com去。这是由Cookie的隐私安全机制决定的。隐私安全机制能够禁止网站非法获取其他网站的Cookie。
正常情况下,同一个一级域名下的两个二级域名如www.helloweenvsfei.com和 images.helloweenvsfei.com也不能交互使用Cookie,因为二者的域名并不严格相同。如果想所有 helloweenvsfei.com名下的二级域名都可以使用该Cookie,需要设置Cookie的domain参数,例如:
Cookie cookie = new Cookie("time","20080808"); // 新建Cookie
cookie.setDomain(".helloweenvsfei.com"); // 设置域名
cookie.setPath("/"); // 设置路径
cookie.setMaxAge(Integer.MAX_VALUE); // 设置有效期
response.addCookie(cookie); // 输出到客户端
读者可以修改本机C:WINDOWSsystem32driversetc下的hosts文件来配置多个临时域名,然后使用setCookie.jsp程序来设置跨域名Cookie验证domain属性。
注意:domain参数必须以点(".")开始。另外,name相同但domain不同的两个Cookie是两个不同的Cookie。如果想要两个域名完全不同的网站共有Cookie,可以生成两个Cookie,domain属性分别为两个域名,输出到客户端。
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。使用时一定要注意。
cookie的不可跨域性
很多网站都会使用Cookie。例如,Google会向客户端颁发Cookie,Baidu也会向客户端颁发Cookie。那浏览器访问Google会不会也携带上Baidu颁发的Cookie呢?或者Google能不能修改Baidu颁发的Cookie呢?
答案是否定的。Cookie具有不可跨域名性。根据Cookie规范,浏览器访问Google只会携带Google的Cookie,而不会携带Baidu的Cookie。Google也只能操作Google的Cookie,而不能操作Baidu的Cookie。
Cookie在客户端是由浏览器来管理的。浏览器能够保证Google只会操作Google的Cookie而不会操作 Baidu的Cookie,从而保证用户的隐私安全。浏览器判断一个网站是否能操作另一个网站Cookie的依据是域名。Google与Baidu的域名 不一样,因此Google不能操作Baidu的Cookie。
需要注意的是,虽然网站images.google.com与网站www.google.com同属于Google,但是域名不一样,二者同样不能互相操作彼此的Cookie。
Unicode编码:保存中文
中文与英文字符不同,中文属于Unicode字符,在内存中占4个字符,而英文属于ASCII字符,内存中只占2个字节。Cookie中使用Unicode字符时需要对Unicode字符进行编码,否则会乱码。
提示:Cookie中保存中文只能编码。一般使用UTF-8编码即可。不推荐使用GBK等中文编码,因为浏览器不一定支持,而且JavaScript也不支持GBK编码。
BASE64编码:保存二进制图片
Cookie不仅可以使用ASCII字符与Unicode字符,还可以使用二进制数据。例如在Cookie中使用数字证书,提供安全度。使用二进制数据时也需要进行编码。
%注意:本程序仅用于展示Cookie中可以存储二进制内容,并不实用。由于浏览器每次请求服务器都会携带Cookie,因此Cookie内容不宜过多,否则影响速度。Cookie的内容应该少而精。
Cookie的安全属性
HTTP协议不仅是无状态的,而且是不安全的。使用HTTP协议的数据不经过任何加密就直接在网络上传播,有被截获的可 能。使用HTTP协议传输很机密的内容是一种隐患。如果不希望Cookie在HTTP等非安全协议中传输,可以设置Cookie的secure属性为 true。浏览器只会在HTTPS和SSL等安全协议中传输此类Cookie。下面的代码设置secure属性为true:
Cookie cookie = new Cookie("time", "20080808"); // 新建Cookie
cookie.setSecure(true); // 设置安全属性
response.addCookie(cookie); // 输出到客户端
提示:secure属性并不能对Cookie内容加密,因而不能保证绝对的安全性。如果需要高安全性,需要在程序中对Cookie内容加密、解密,以防泄密。
JavaScript操作Cookie
Cookie是保存在浏览器端的,因此浏览器具有操作Cookie的先决条件。浏览器可以使用脚本程序如 JavaScript或者VBScript等操作Cookie。这里以JavaScript为例介绍常用的Cookie操作。例如下面的代码会输出本页面 所有的Cookie。
由于JavaScript能够任意地读写Cookie,有些好事者便想使用JavaScript程序去窥探用户在其他网 站的Cookie。不过这是徒劳的,W3C组织早就意识到JavaScript对Cookie的读写所带来的安全隐患并加以防备了,W3C标准的浏览器会 阻止JavaScript读写任何不属于自己网站的Cookie。换句话说,A网站的JavaScript程序读写B网站的Cookie不会有任何结果。
案例:永久登录
如果用户是在自己家的电脑上上网,登录时就可以记住他的登录信息,下次访问时不需要再次登录,直接访问即可。实现方法是把登录信息如账号、密码等保存在Cookie中,并控制Cookie的有效期,下次访问时再验证Cookie中的登录信息即可。
保存登录信息有多种方案。最直接的是把用户名与密码都保持到Cookie中,下次访问时检查Cookie中的用户名与密码,与数据库比较。这是一种比较危险的选择,一般不把密码等重要信息保存到Cookie中。
还有一种方案是把密码加密后保存到Cookie中,下次访问时解密并与数据库比较。这种方案略微安全一些。如果不希望保存密码,还可以把登录的时间戳保存到Cookie与数据库中,到时只验证用户名与登录时间戳就可以了。
这几种方案验证账号时都要查询数据库。
本例将采用另一种方案,只在登录时查询一次数据库,以后访问验证登录信息时不再查询数据库。实现方式是把账号按照一定的规则加密后,连同账号一块保存到Cookie中。下次访问时只需要判断账号的加密规则是否正确即可。本例把账号保存到名为account的Cookie中,把账号连同密钥用MD1算法加密后保存到名为ssid的Cookie中。验证时验证Cookie中的账号与密钥加密后是否与Cookie中的ssid相等。相关代码如下:
代码1.8 loginCookie.jsp
- <%@ page language="java"pageEncoding="UTF-8" isErrorPage="false" %>
- <%! // JSP方法
- private static final String KEY =":cookie@helloweenvsfei.com";
- // 密钥
- public final static String calcMD1(Stringss) { // 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[] = newchar[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 newString(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 = newInteger(request.getParameter("timeout"));
- // 获取timeout参数
- String ssid =calcMD1(account + KEY); // 把账号、密钥使用MD1加密后保存
- CookieaccountCookie = 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;
- }
- elseif("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 {%>
- <formaction="${ 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>
- <% } %>
登录时可以选择登录信息的有效期:关闭浏览器即失效、30天内有效与永久有效。通过设置Cookie的age属性来实现,注意观察代码
提示:该加密机制中最重要的部分为算法与密钥。由于MD1算法的不可逆性,即使用户知道了账号与加密后的字符串,也不可能解密得到密钥。因此,只要保管好密钥与算法,该机制就是安全的。