您的位置:首页 > 运维架构 > 网站架构

使用Cookie的跨域访问实现多网站间记住密码

2021-02-03 11:10 696 查看

前两天,有个刚入职的兄弟问了一个问题:他们公司有两个网站,领导想让他实现两个网站之间"记住密码"功能的互认,即对于同一个客户端(浏览器)只要访问A网站时选择了记住密码,那么下次他不管访问A网站还是B网站都会记住密码,想让给他提供一个思路或者解决方案。
我想记住密码正是Cookie最擅长的,今天正好有时间,就来谈谈Cookie,帮他整理一下思路,也希望能帮助到更多的人。

首先来说一下Cookie简单使用案例:

我们都很熟悉Cookie,就是服务器向客户端保存的纯文本信息。网站通常用Cookie来向客户端保存一些字符串信息,以便需要的时候再获取,经典应用就是实现网站的记住密码或者免登录功能:

客户端向网站的服务器发送登录请求,并携带账号和密码数据;
网站的服务器校验账号密码正确以后,返回响应并把账号密码以cookie的形式写到客户端;
之后该客户端再次向网站发起请求会自动带上网站存储在本地的cookie;
网站的服务器从Cookie中获取账号密码数据后,返回登陆成功界面或者自动填写账号密码。

看代码:===>
登录请求提交到服务器时:如果账号密码都正确并且也选择了记住密码,就会把账号密码以Cookie的形式写到客户端:LoginServlet.java

public class LoginServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String loginAct=req.getParameter("loginAct");
String loginPwd=req.getParameter("loginPwd");
String isRemPwd=req.getParameter("isRemPwd");

//登录失败,跳转到失败页面
if(!("cohouse".equals(loginAct)&&"123".equals(loginPwd))){
resp.sendRedirect("fail.jsp");
}else{//登录成功,跳转到登录成功页面
//假设参数isRemPwd为true标识客户单选择了记住密码
//如果选择了记住密码,就往客户端写cookie
if("true".equals(isRemPwd)){
Cookie actCookie=new Cookie("loginAct",loginAct);
actCookie.setMaxAge(10*24*60*60);//记10天
resp.addCookie(actCookie);//写到客户端
Cookie pwdCookie=new Cookie("loginPwd",loginPwd);
pwdCookie.setMaxAge(10*24*60*60);//记10天
resp.addCookie(pwdCookie);//写到客户端
}

resp.sendRedirect("success.jsp");
}
}
}

每次访问网站时:都会获取客户端的Cookie,查看有没有账号密码相关的Cookie,如果有的话,可以进行自动登录,或者把账号密码自动填到输入框里面:login.jsp

<form action="LoginServlet" method="post">
用户名:<input type="text" name="loginAct" value="${cookie.loginAct.value}"><br>
密码:  <input type="password" name="loginPwd" value="${cookie.loginPwd.value}"><br>
<input type="checkbox" name="isRemPwd">记住密码<br>
<input type="submit" value="登录">
</form>

以上是Cookie最简单的应用,显然还不足以解决那位兄弟跨网站记住密码的问题,下边来看看Cookie的特性:

Cookie的特性:

首先,Cookie不可以跨域名访问

很多网站都会使用Cookie。例如:当你网上购物时,京东会向你的浏览器写Cookie,淘宝当然也会,下次访问时,你都会把这些Cookie再给他们带回去。但这里的“带回去”不可能是简简单单地把你客户端中所有的Cookie都带过去,即如果用户访问淘宝时也会连同京东写的cookie带过去,或者说访问京东时也会连同淘宝写的cookie带过去,如果真是这样的话,那这个世界可就真是乱了套了!不天下大乱估计也差不多!
当然,协议的设计者们不可能这么干。根据浏览器规范的"同源策略",未经允许,Cookie不可以跨域访问。客户端浏览器访问淘宝时,只会携带淘宝的Cookie,而不会携带京东或者其它网站的Cookie。所以淘宝只能操作淘宝的Cookie,京东也只能操作京东的Cookie,这样就保证了网站和客户的隐私安全。
实际上,浏览器会通过域名来判断两个Cookie是不是属于同一个网站,京东和淘宝的域名不一样,因此京东不能操作淘宝的Cookie,反之亦然。
需要强调的是,域名必须严格相同,Cookie才能互相访问。例如,images.jd.com和pages.jd.com虽然都是属于京东、一级域名相同、但是二级域名并不严格相同,两网站的Cookie也不能互相操作彼此。
Cookie跨域访问的解决:正是由于Cookie的这种不可跨域访问性,使得跨网站记住密码功能貌似不能轻易实现。但话又说回来了,规则都是人定的,在这个世界上任何规则都不是绝对的,如果有的Cookie确实需要其它网站访问,也不是不可以,但必须经过Cookie创建者的允许,代码上只需要设置Cookie的domain属性

Cookie actCookie=new Cookie("loginAct",loginAct);
actCookie.setDomain("cohouse.com");//设置域名
actCookie.setMaxAge(10*24*60*60);//记10天
resp.addCookie(actCookie);//写到客户端
Cookie pwdCookie=new Cookie("loginPwd",loginAct);
pwdCookie.setDomain("cohouse.com");//设置域名
pwdCookie.setMaxAge(10*24*60*60);//记10天
resp.addCookie(pwdCookie);//写到客户端

这样,所有并且也只有域名cohouse.com及其名下的二级域名可以使用该Cookie,比如:这里cohouse.com、www.cohouse.com和test.cohouse.com等域名都可以访问actCookie,但是www.jd.com就不能访问。读者可以修改本机C:\WINDOWS\system32\drivers\etc下的hosts文件来配置多个临时域名,然后使用程序来设置跨域名Cookie验证domain属性。
需要注意的是,如果你使用的是tomcat8.0以前的服务器,domain属性必须以点(".")开头,例如:actCookie.setDomain(".cohouse.com")。另外,设置了domain的两个Cookie,即使name相同但domain不同,也是两个完全不同的Cookie。
另外,domain属性默认为请求地址的域名,如网址为www.cohouse.com/test写Cookie,那么domain默认为www.cohouse.com。
到此为止,那位兄弟要实现的两个网站跨域记住密码的功能就迎刃而解了:

如果他两个网站部署在同一台服务器上,由于域名相同,Cookie采用默认域名,无需手动设置domain属性,只要Cookie路径允许(Cookie的路径后边会详细说),就可以轻易而一举实现了。如果他两个网站不是部署在同一台服务器上(这种概率很大),两台服务器的域名完全不同,则可以生成两个Cookie,domain属性分别为两个域名,写到客户端。

其次,Cookie的有效期

每一个Cookie都有着明确的有效期。Cookie的maxAge属性决定着Cookie的有效期,单位为秒。Cookie提供两个方法getMaxAge()和setMaxAge()分别来读取和设置maxAge的值。

Cookie cookie=new Cookie("loginAct","cohouse");//创建Cookie
cookie.setMaxAge(10*24*60*60);//设置最大生命周期为10天
resp.addCookie(cookie);//写到客户端

如果maxAge为正数,表示该Cookie写到客户端之后maxAge秒自动失效。浏览器会maxAge为正数的Cookie持久化,即写到对应的Cookie文件中。无论客户关闭了浏览器还是电脑,只要还在maxAge秒之前,登录网站时该Cookie仍然有效。
如果maxAge为0,表示该Cookie写到客户端之后马上失效,通常用来删除Cookie。
如果maxAge为负数,表示该Cookie为临时性Cookie,仅在本浏览器窗口以及本窗口打开的子窗口内有效,关闭窗口后该Cookie即失效。Cookie的maxAge默认值是-1。
需要注意的是,我们在客户端操作Cookie时,maxAge属性是不可读的,maxAge属性值也不会被再次携带到服务器。浏览器访问服务器时只会携带Cookie的name属性和value属性。换句话说,maxAge仅仅用来给浏览器判断Cookie是否过期使用的,别无它用!所以,我们在打印所有Cookie的maxAge属性时,一律都是-1:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>打印所有Cookie</title>
</head>
<body>
<%
Cookie[] cs=request.getCookies();
for(Cookie c:cs){
out.write("<h5>"+c.getName()+":"+c.getValue()+":"+c.getMaxAge()+"<h5>");
}
%>
</body>
</html>

第三,Cookie的路径

domain属性决定能够访问Cookie的域名,而path属性决定允许访问Cookie的资源路径(ContextPath)。例如:有三个目录如下:/test/、/test/aa/、/test/bb/,现在生成两个Cookie,path分别为/test/和/test/aa/:

Cookie cookie1=new Cookie("cookie1","11111111");
cookie1.setMaxAge(10*24*60*60);//记10天
cookie1.setPath(req.getContextPath()+"/test/");
resp.addCookie(cookie1);

Cookie cookie2=new Cookie("cookie2","22222222");
cookie2.setMaxAge(10*24*60*60);//记10天
cookie2.setPath(req.getContextPath()+"/test/aa");
resp.addCookie(cookie2);

那么,/test/目录及其子目录下所有页面都可以访问到cookie1,而/test/目录和/test/bb/目录不能访问cookie2。这是因为Cookie只能被它的路径及其子路径下的页面访问,不能被其它的路径访问。
Cookie的默认路径,即如果不设置path,则Cookie默认的path值是“/项目名/当前路径的上一级”。例如:如果当前路径是/myweb/test/SetCookieServlet(myweb是项目名),则生成的Cookie默认路径就是/myweb/test/。
Cookie就是这样通过domain属性和path属性结合控制其访问权限,前者从域的层面进行宏观控制,后者从域内部路径的层面进行更精确控制,两者结合起来,灵活运用,方能得心应手。

第四,JavaScript操作Cookie

Cookie是保存在浏览器端的,因此浏览器理所当然地能够操作Cookie。浏览器可以使用JavaScript、VBScript等脚本程序操作Cookie。例如:以使用JavaScript为例打印出本页面所有的Cookie:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>JavaScript打印所有Cookie</title>
<script type="text/javascript">
document.write(document.cookie);
</script>
</head>
<body>
</body>
</html>

显然,JavaScript是通过document对象的cookie属性来操作Cookie的。说明一下,document.cookie是一个特定格式的字符串,它使用分号(“;”)连接多个cookie信息,我们可以通过该字符串在js中完成对cookie的增删改查。
最后强调一点,JavaScript同样也只能操作本页面所能访问到的Cookie,即受Cookie的domain和path属性值的限制,而不能够任意地读写各个网站的Cookie,否则也会乱套。这种基于规则的自由才是真正的自由,不是吗?
以上,就是我基于那位兄弟的问题而产生的一些思考和总结,把知识分享出去,帮助别人,成就自己,跟大家共勉。JAVA技术交流QQ群29259682,欢迎来我的咖啡屋玩JAVA品咖啡。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: