说明
(1)JDK版本:1.8
(2)Spring Boot 2.0.6
(3)Spring Security 5.0.9
(4)Spring Data JPA 2.0.11.RELEASE
(5)hibernate5.2.17.Final
(6)MySQLDriver 5.1.47
(7)MySQL 8.0.12
需求缘起
对于用户还是比较懒惰的,我们不可能要求要求用户访问网站每次都需要输入用户名和密码,于是就有了Remember-Me的需求了。
一、什么是Remember-Me?
Remember-Me是指网站能够在 Session 之间记住登录用户的身份,具体来说就是成功认证一次之后在一定的时间内我可以不用再输入用户名和密码进行登录了,系统会自动登录。
二、Remember-Me原理
通常是通过服务端发送一个 cookie 给客户端浏览器,下次浏览器再访问服务端时服务端能够自动检测客户端的cookie,根据 cookie 值触发自动登录操作。对于SpringSecurity的cookie的默认名称是:remember-me,举例说明:
remember-me:
YWRtaW46MTU1NTA0MTYyNTIxOToyYzdkNDIwMWUzNmRmODc5MmMzNDY0MjJmNTdiOGJmMA
三、Remember-Me实现方式
Spring Security针对于 Remember-Me 功能有两种实现。
(1)一种是简单的使用加密来保证基于 cookie 的 token 的安全;
(2)另一种是通过数据库或其它持久化存储机制来保存生成的token。
注意的是两种实现都需要一个 UserDetailsService。如果你使用的AuthenticationProvider 不使用 UserDetailsService,那么记住我将会不起作用,除非在你的 ApplicationContext 中拥有一个 UserDetailsService类型的 bean
3.1 基于简单加密 token 的方法
当用户选择了记住我成功登录后,Spring Security 将会生成一个 cookie 发送给客户端浏览器。cookie 值由如下方式组成:
base64(username+":"+expirationTime+":"+md5Hex(username+":"+expirationTime+":"+password+":"+key))
username:登录的用户名。
password:登录的密码。
expirationTime:token 失效的日期和时间,以毫秒表示。
key:用来防止修改 token 的一个 key。
这样用来实现 Remember-Me 功能的 token 只能在指定的时间内有效, 且必须保证 token 中所包含的 username、password 和 key 没有被改变才行。需要注意的是,这样做其实是存在安全隐患的,那就是在用户获取到实现记住我功能的 token 后,任何用户都可以在该 token 过期之前通过该 token 进行自动登录。如果用户发现自己的 token 被盗用了,那么他可以通过改变自己的登录密码来立即使其所有的记住我 token 失效。如果希望我们的应用能够更安全一点,可以使用接下来要介绍的持久化token 方式,或者不使用 Remember-Me 功能,因为 Remember-Me 功能总是有点不安全的。
3.2 基于持久化 token 的方法
持久化 token 的方法跟简单加密 token 的方法在实现 Remember-Me 功能上大体相同,都是在用户选择了 “记住我” 成功登录后,将生成的 token 存入 cookie 中并发送到客户端浏览器,待到下次用户访问系统时,系统将直接从客户端cookie 中读取 token 进行认证。所不同的是基于简单加密 token 的方法,一旦用户登录成功后,生成的 token 将在客户端保存一段时间,如果用户不点击退出登录,或者不修改密码,那么在 cookie 失效之前,他都可以使用该 token 进行登录,哪怕该 token 被别人盗用了,用户与盗用者都同样可以进行登录。而基于持久化 token 的方法采用这样的实现逻辑:
(1)用户选择了 “记住我” 成功登录后,将会把 username、随机产生的序列号、生成的 token 存入一个数据库表中,同时将它们的组合生成一个 cookie 发送给客户端浏览器。
(2)当下一次没有登录的用户访问系统时,首先检查 cookie,如果对应 cookie 中包含的username、序列号和 token 与数据库中保存的一致,则表示其通过验证,系统将重新生成一个新的 token 替换数据库中对应组合的旧 token,序列号保持不变,同时删除旧的 cookie,重新生成包含新生成的 token,就的序列号和username 的 cookie 发送给客户端。
(3)如果检查 cookie 时,cookie 中包含的 username 和序列号跟数据库中保存的匹配,但是 token 不匹配。这种情况极有可能是因为你的 cookie 被人盗用了,由于盗用者使用你原本通过认证的 cookie进行登录了导致旧的 token 失效,而产生了新的 token。这个时候Spring Security 就可以发现 cookie 被盗用的情况,它将删除数据库中与当前用户相关的所有 token 记录,这样盗用者使用原有的 cookie 将不能再登录,同时提醒用户其帐号有被盗用的可能性。
(4)如果对应 cookie 不存在,或者包含的 username 和序列号与数据库中保存的不一致,那么将会引导用户到登录页面。
从以上逻辑我们可以看出持久化 token 的方法比简单加密 token 的方法更安全,因为一旦你的 cookie 被人盗用了,你只要再利用原有的 cookie 试图自动登录一次,原有的 token 将失效导致盗用者不能再使用原来盗用的 cookie 进行登录了,同时用户可以发现自己的 cookie 有被盗用的可能性。但因为 cookie 被盗用后盗用者还可以在用户下一次登录前顺利的进行登录,所以如果你的应用对安全性要求比较高就不要使用 Remember-Me 功能了。
四、总结
对于Remember-Me的功能,SpringSecurity提供了两种方式:
(1)基于简单加密token的方法。
(2)基于持久化token的方法。
对于这两种方式的共同点就是Spring Security会根据username等信息生成cookie返回给浏览器,浏览器进行存储。对于简单加密token的方法,服务器端并不保存之后的数据,对于基于持久化token的方法,会保存用户的一些基本信息username、series
、token、last_used。
本节就先介绍到这里,下节我们将在我们的项目中加入Remember-Me进行实战演练下。