2019独角兽企业重金招聘Python工程师标准>>>
这篇主要说明基于COOKIE的单点登录实现,以及COOKIE的一些特性以及使用说明。
1、COOKIE是什么,如何工作的
在程序中,会话跟踪是很重要的事情。理论上,一个用户的所有请求操作都应该属于同一个会话,而另一个用户的所有请求操作则应该属于另一个会话,二者不能混淆。例如,用户A在超市购买的任何商品都应该放在A的购物车内,不论是用户A什么时间购买的,这都是属于同一个会话的,不能放入用户B或用户C的购物车内,这不属于同一个会话。而Web应用程序是使用HTTP协议传输数据的。HTTP协议是无状态的协议。一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接。这就意味着服务器无法从连接上跟踪会话。即用户A购买了一件商品放入购物车内,当再次购买商品时服务器已经无法判断该购买行为是属于用户A的会话还是用户B的会话了。要跟踪该会话,必须引入一种机制。
COOKIE就是这样的一种机制。它可以弥补HTTP协议无状态的不足。在Session出现之前,基本上所有的网站都采用COOKIE来跟踪会话。
COOKIE意为“甜饼”,是由W3C组织提出,最早由Netscape社区发展的一种机制。目前COOKIE已经成为标准,所有的主流浏览器如IE、Netscape、Firefox、Opera等都支持COOKIE。
2、现实生活中类似于COOKIE的举例
例如你所在的城市可能有很多便利店(web服务器),便利店一般都会举行一些活动,例如积满15次消费金额在20元以上,送毛绒公仔一只。但是客户(浏览器)特别多,便利店不可能每个都记住,于是便利店就制作一个卡片(COOKIE)交给客户。之后每次客户来买东西的人时候就把卡片提交给便利店(web服务器),于是便利店就知道你目前的状态信息,这样就做到了跟踪会话。
从上面可以看出,COOKIE是由浏览器管理的,web服务器将数据交给浏览器,浏览器可以把数据保存起来,等到下次访问的时候自动提交。
如上图中,在整个交互中的流程如下:
第一步:浏览器发送信息(例如:用户名密码)提交给服务器。
第二步:服务器接收到之后,发送一个命令给浏览器,告诉他你要把相关的信息存到COOKIE里面。
第三步:如果是零时性的就存于浏览器内存中,重启浏览器信息就消失了。
第四步:如果是需要存很久,例如两周之内自动登陆这种需求,则将信息存于硬盘上。
第五步:当在此访问浏览器时,则自动将COOKIE发送给服务器。
3、各浏览器都把COOKIE放到了哪里
以Windows7为例:
IE浏览器:%APPDATA%\Microsoft\Windows\COOKIEs\ 目录中的xxx.txt文件 (IE浏览器分开存放的)。
火狐浏览器:%APPDATA%\Mozilla\Firefox\Profiles\ 目录中的xxx.default目录,名为COOKIEs.sqlite的文件。
谷歌浏览器:%LOCALAPPDATA%\Google\Chrome\User Data\Default\ 目录中,名为COOKIEs的文件。
在IE浏览器中,IE将各个站点的COOKIE分别保存为一个XXX.txt这样的纯文本文件(文件个数可能很多,但文件大小都较小);而Firefox和Chrome是将所有的COOKIE都保存在一个文件中(文件大小较大),该文件的格式为SQLite3数据库格式的文件。
4、实际开发项目中如何使用代码操作COOKIE
在Java中,Web项目提供了一个类:javax.servlet.http.COOKIE 该类就与COOKIE对应。下面给出Servlet中操作COOKIE示例代码:
package com.csdn.cas;import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.COOKIE;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class LoginServlet extends HttpServlet {private static final long serialVersionUID = 1L;@Overridepublic void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 获取请求参数String userName = req.getParameter("userName");String passwd = req.getParameter("passwd");// 创建COOKIE对象COOKIE userInfoCOOKIE = new COOKIE("userInfo", userName + ":" + passwd);// 返回给浏览器的数据中添加COOKIE信息resp.addCOOKIE(userInfoCOOKIE);}
}
上面就是简单的操作COOKIE的代码,我们将这个servlet部署到tomcat中,使用谷歌,并观察相关的COOKIE信息(使用F12,有调试工具)。
观察第一次的访问情况,在响应头里面多出来一个Set-COOKIE:的东东,浏览器就根据这个这个COOKIE信息保留了下来。
当再一次访问这个网站时,则请求头中多了第一次返回的COOKIE信息,响应中之所以还有Set-COOKIE,是因为我们访问的是同一个代码。
如果你把浏览器关了,在访问这个地址,那么请求中是不会带有COOKIE信息的,因为浏览器只是零时性的将数据保存在内存中。如果需要保存在硬盘上则需要特殊处理。
5、COOKIE的特性
5.1、COOKIE不能跨域
例如:你访问www.baidu.com,百度给你发了一个COOKIE,你在访问www.google.com你是不能把百度发给你的COOKIE带到谷歌的服务器上的。
需要特别注意的是:images.google.com与www.google.com同属于google,但是他们的域名不一样,二者同样也不能操作彼此的COOKIE,浏览器在访问的时候,会根据访问地址自动携带相关域名的COOKIE过去。
注意:有时候你会发现有些特殊的网站,类似你登录了www.google.com但是你当问images.google.com时,www.google.com的COOKIE被带到了images.google.com,这是因为做了特殊的处理,下面会详细说。
5.2、其他相关特性请参照博客
传送门:http://blog.csdn.net/fangaoxin/article/details/6952954/
6、利用COOKIE的特性实现单点登录的原理
上图就是一个利用COOKIE做单点登录的原理图,案例为用户dgh(密码123)登录www.qiandu.com,之后又登录mail.qiandu.com。流程如下:
第一步:用户输入用户名/密码(dgh/123)登录到www.qiandu.com。
第二步:www.qiandu.com处理登录逻辑,并且将用户信息通过COOKIE的方式返回到客户端(最好加密用户信息)。
第三步:用户访问mail.qiandu.com,浏览器自动将用户信息携带到mail.qiandu.com,通过过滤器(filter)处理用户的登录请求。
第四步:过滤器从COOKIE中获取用户信息,登录,返回用户访问界面。这样用户就只登录一次,就访问了两个网站了。
7、COOKIE实现单点登录的代码实现
1、首先是一个登录的Servlet,简单实现,只要用户名密码相同则登录成功,并将信息写到COOKIE中。
package com.csdn.cas;import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.COOKIE;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;public class LoginServlet extends HttpServlet {private static final long serialVersionUID = 1L;@Overridepublic void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 获取请求参数String userName = req.getParameter("userName");String passwd = req.getParameter("passwd");resp.setCharacterEncoding("UTF-8");resp.setContentType("text/html;charset=UTF-8");HttpSession session = req.getSession();// 只有用户名与密码相同,则登录成功if(userName.equals(passwd)){// 创建COOKIE对象COOKIE userInfoCOOKIE = new COOKIE("userInfo", userName + ":" + passwd);// 这里很重要,不设置无法夸子域 这里最好以 .开头,例如.qiandu.com// 谷歌浏览器自动给他添加了.userInfoCOOKIE.setDomain("qiandu.com");// 返回给浏览器的数据中添加COOKIE信息resp.addCOOKIE(userInfoCOOKIE);session.setAttribute("userName", userName + ",登录成功");}else {session.setAttribute("userName", userName + ",登录失败");}req.getRequestDispatcher("/index.jsp").forward(req, resp);}
}
2、写一个Filter,过滤处理所有的请求,如果是登录请求则到登录页,否则尝试登陆。
package com.csdn.cas;import java.io.IOException;import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.COOKIE;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;public class LoginFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException { }@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {HttpServletRequest req = (HttpServletRequest)request ;HttpServletResponse resp = (HttpServletResponse) response ;COOKIE[] COOKIEs = req.getCOOKIEs();resp.setCharacterEncoding("UTF-8");resp.setContentType("text/html;charset=UTF-8");HttpSession session = req.getSession();Object userInfo = session.getAttribute("userName");if(userInfo == null){ // 没登录if(COOKIEs != null){ // 有COOKIEfor(COOKIE COOKIE : COOKIEs){if("userInfo".equals(COOKIE.getName())){String[] value = COOKIE.getValue().split(":");String userName = value[0];String passwd = value[1];// 只有用户名与密码相同,则登录成功if(userName.equals(passwd)){// 创建COOKIE对象session.setAttribute("userName", userName + ",从filter登录成功");}else {session.setAttribute("userName", userName + ",从filter登录失败");}}}} else {// 这里应该跳转到登录页面}}chain.doFilter(request, response);}@Overridepublic void destroy() { }
}
3、index.jsp页面就展示登录用户用户名,如果没有登录就显示null
Hello !
<%&#61;session.getAttribute("userName")%>
注意&#xff1a;
上面说过COOKIE是不能跨域的&#xff0c;通过特殊设置setDomain()可以做到夸同一个大域下的两个子域。例如&#xff0c;www.qiandu.com与mail.qiandu.com这是两个域&#xff0c;但是他们都是qiandu.com下的两个子域&#xff0c;则可以通过设置COOKIE.setDomain("qiandu.com")从而达到夸域。但是对于www.baidu.com与www.google.com就没得办法了。
8、测试COOKIE的单点登录
修改windows的hosts文件&#xff0c;添加如下内容&#xff1a;
测试步骤&#xff1a;
第一步&#xff1a;启动服务器&#xff0c;通过www.qiandu.com访问服务&#xff0c;显示登陆失败
第二步&#xff1a;通过get方式登录&#xff1a;http://www.qiandu.com/login?userName&#61;dgh&passwd&#61;dgh
第三步&#xff1a;登录http://mail.qiandu.com&#xff0c;发现第一次登陆mail.qiandu.com用户名密码就被COOKIE带过来了。
然后页面显示&#xff1a;
注意&#xff1a;为什么www.qiandu.com的COOKIE能带到mail.qiandu.com呢&#xff1f;我们从浏览器中查看COOKIE信息即可得知&#xff0c;域是www.qiandu.com与mail.qiandu.com的公共部分&#xff0c;所以浏览器认为&#xff0c;他们是一起的&#xff0c;COOKIE交流是安全的。而且我代码中写的是qiandu.com&#xff0c;浏览器自动在前面加了一个点
案例代码&#xff1a;http://download.csdn.net/detail/readiay/9654716
CAS统一认证的原理&#xff1a;http://blog.csdn.net/readiay/article/details/52856510