本文讨论几种记住我功能的实现方式。
原理:用户登录后,服务端为用户生成一个Token,并放入客户端Cookie中。下次用户登录,服务端验证Cookie中的Token并自动登录。
- 简单的Token生成方法
Token=MD5Hex(username+分隔符+expiryTime+分隔符+password)
CookieValue=Base64(username+分隔符+expiryTime+分隔符+Token)
username:用户名。
password:用户密码。
password:用户密码。
expiryTime:Token的失效时间,以毫秒表示。
该方法生成的Token不需要持久化,每次生成时失效时间不同,可保证Token不同(也可以在Token中增加一些变量,如系统配置的key)。用户第一次登陆时生成Token;第二次打开系统时,根据CookieValue中username查询数据库,并重新生成Token,验证用户提交Token是否正确,如果正确成功登入系统,如果不正确,调整到登陆页面。
存在安全问题。当用户Cookie被窃取(HttpOnly增加安全性),任何用户都可以在Token失效之前登录。发现被盗用,可以修改password来使Token失效,或者系统在用户每次登入系统时都重新生成Token。
缺点:由于Token没有持久化,所以需要从已知的变量(如username、password、expiryTime)重新生成来验证,password即使加密放入前台,也不是一个好的解决方法。纵然不使用password,也需要其他变量来保证Token的可靠性。
- 持久化的Token生成方法
Token的生成规则可以自定义,只要保证每次username的Token不一样就可,例如采用16位随机数(如Java的SecureRandom)。规则同上面类似,只是将Token与用户信息解耦。但是安全问题依旧存在。
- 持久化Token生成方法并验证Cookie是否被窃取
Token:随机生成策略,只要保证username唯一性。
series:登录序列号,随机生成策略。用户输入用户名和密码登录时,该值重新生成。使用remember-me功能,该值保持不变,重新生成Token。
expiryTime:Token过期时间。
1)每次用户选择记住我并登录后,都重新随机生成series、Token,保存到数据库和前端Cookie中。
2)下一次用户再次访问系统,情况一:前台Cookie中保存的series值在数据库中不存在,跳转到登录页面。情况二:series、Token与数据库中一致,则仍为合法用户,并保持series不变,重新随机生成Token,覆盖前台Cookie值。情况三:series在数据库中存在,但Token不一致,说明Cookie被窃取。
情况一、同用户相同浏览器访问系统
步骤
|
用户A浏览器
|
1 | A登录成功,series=seriesA,token=tokenA |
2 | 在Token失效之前该浏览器访问系统,合法,生成series=seriesA,token=tokenB。 |
3 | Token失效,或者用户重新登录,生成series=seriesB,token=tokenC |
情况二、同用户不同浏览器访问系统
步骤
|
用户A浏览器X
|
用户A浏览器Y
|
1 | 浏览器X登录成功,series=seriesA,token=tokenA | |
2 | 浏览器Y访问,无series信息,用户重新登录,series=seriesB,token=tokenB | |
3 | 浏览器X再次问题,seriesA失效,需要重新登录 |
情况三、Cookie被盗
步骤 | 用户A | 黑客B |
1 | A登录成功,生成series=seriesA,token=tokenA | |
2 | 盗取A Cookie,使用series=seriesA,token=tokenA登入系统成功,重新生成series=seriesA,token=tokenB | |
3 | A再次访问,发现serieA相同,但是Token不同,提醒用户Cookie被盗 |
安全问题依然存在。当然,可以考虑用户访问等级,如用户名密码登录级别最高,而对于Token记住我登录次之,对于一些重要的业务操作时(如支付),还是需要进行用户身份认证。
还可以考虑用户IP,但现在移动用户,经常切换WiFi和网络,所以IP也会发生变化。例如手机银行(如招行客户端)切换网络后,需要重新登录,提高安全性。
还可以考虑,用户常用IP、城市等安全策略。
还有Shiro也实现了该记住我功能,但是本人没有使用过,等退后学习了再来更新。