此篇随笔记录了Remember-Me实现过程当中出现的问题和解决方案,以及相关的思考。前端
1. RememberMe是什么?数据库
RememberMe意为记住我,对应登陆界面的那个勾选项。另外一种说法,就是自动登陆。浏览器
2. 那什么又是自动登陆呢?服务器
咱们知道Tomcat或者其余Servlet容器的会话都是有时限的,好比Tomcat的会话时间为30分钟,30分钟后,会话将被清除,这时候就不知足登陆状态了。那你发出下一个请求,按照正常逻辑就会被拦截下来,告知你没有登陆,而后跳转到登陆界面让你从新登陆。自动登陆作的就是,当会话过时后,请求会根据Cookie信息实现透明登陆。也就是从新又登陆了一遍,产生了一个新的会话。可是在用户看来,他根本不知道发生了什么,并且从新登陆的过程当中不须要再让用户输入帐号密码。cookie
3. 为何不把会话时间调大一点呢?网站
Tomcat的会话时间是可配置的。你能够设置成3天或者更久。可是这样就加大了服务器的开销。你设置3天,意味着一个HttpSession将会由Tomcat保留3天。(这里究竟是保存在内存仍是磁盘尚不肯定,运行时内存的可能比较大),前面说了RemememberMe实际上是自动登陆,也就是说,它不会影响会话存在的时间。并且新会话的产生必然由新的请求触发。编码
若是用户登陆后只浏览了10分钟,就挂着不动了,在都没有进行登出操做的状况下。对比一下两种方案的最大开销(服务端保留无用会话的时间)加密
第一种:3天会话时间 => 3天 - 10分钟 => 3*24*60 - 10 = 4310 分钟 spa
第二种:30分钟会话时间(默认) => 30 - 10 分钟 = 20分钟code
4. RememberMe的原理是什么?
本质上就是Cookie。在登陆成功后,若是用户有勾选“记住我”,则服务端在响应中加上Remember-Me的cookie,让浏览器保存一段时间,如7天。在7天以内,若是用户会话过时,可凭此实现透明的从新登陆。
5. Cookie中包含哪些内容呢?
1. 用户帐号 => 否则没法知道谁要登陆,并且须要依此获取密码,生成新签名进行比对
2. 过时时间 => 过时的时间节点,即若是浏览器没有及时自动清除此cookie,服务端收到后要据此删除。
3. 与密码有关的签名 => 若是没有密码的相关信息,那就很容易地经过伪造cookie,来登陆其余人的帐号。可是密码又不能是明文,必须通过加密。并且不能或者不能太容易被解开。
6. Spring Security中的Cookie格式
Base64(username:expireTime:MD5(username:expireTime:password:secretKey))
其中,签名signature由username、expireTime、password、secretKey组成的字符串,进过MD5加密而成。随后再整合username、expireTime利用Base64编码而成,Base64是可解码的。最终结果,基本上就是一条乱码了。
7. Cookie有了,服务端要怎么处理呢?
那就是Filter的事情了。须要定义一个Remember Me的Filter专门处理这个Cookie。值得一提的是,校验Cookie从新认证的过程仍是要查数据库的,因此应该作到只有在必要的时候,即会话过时的时候,才执行校验操做。
8. Remember Me Filter与Login Filter的兼容
Login Filter所作的就是,让没有登陆的用户,必须登陆后才能处理请求。通常会直接返回错误码,让前端跳转到登陆页,或者直接重定向到登陆页。因此Remeber Me Filter确定要放置在Login Filter以前。即会话过时后,先进行的应该是自动登陆。
9. 登出后,Cookie的删除
用户执行登出操做后,确定要删除浏览器的Cookie。不然,在Cookie过时以前,仍是能够经过自动登陆的方式,进入网站。可是,你不可能要求用户去操做浏览器,删除Cookie。这些操做应该由服务端完成。可是HTTP协议只有Set-Cookie的头,却没有相似Remove-Cookie这样的头。HttpServletResponse也没有明确的API能够删除客户端的Cookie。后来想到便可以经过添加不保存的同名Cookie来覆盖要删除的Cookie,后来参考了下Security的实现,发现它也是这么作的。代码以下:
public void logout(HttpServletRequest request, HttpServletResponse response) { //利用覆盖的方法删除客户端的remember-me cookie Cookie cookie = new Cookie("remember-me", (String)null); cookie.setMaxAge(0); response.addCookie(cookie); request.getSession().invalidate(); }