关于网站的安全设计,通常是有一些矛盾点的。我们在作为某些系统开发者的同时,也在充当着另外一些系统的用户,一些感同身受的东西可以带来很多思考。
5.1、为什么需要自动登录
当我们在某个网站上注册账号时,网站会对我们设置的登录密码提出要求。例如,有的网站要求使用固定位数的纯数字密码,有的网站则强制要求用户使用英文+数字组合成的密码,甚至要求加一些特殊符号来组成密码。总体而言,设定一个密码并不困难,真正的困难总是在下次登录时才会遇到。要么想不出网站要求的密码格式是什么,要么还原不了设置密码时的思维状态。总之,在几次尝试登录失败之后,大部分人会选择找回密码,从而再次陷入如何设置密码的循环里。
为了尽可能减少用户重新登录的频率,在系统开发之初就需要考虑加入可以提升用户登录体验的功能。自动登录便是这样一个会给用户带来便利,同时也会给用户带来风险的体验性功能。
自动登录是将用户的登录信息保存在用户浏览器的cookie中,当用户下次访问时,自动实现校验并建立登录态的一种机制。
Spring Security提供了两种非常好的令牌:
◎ 用散列算法加密用户必要的登录信息并生成令牌。
◎ 数据库等持久性数据存储机制用的持久化令牌。
散列算法在Spring Security中是通过加密几个关键信息实现的。
其中,expirationTime指本次自动登录的有效期,key为指定的一个散列盐值,用于防止令牌被修改。通过这种方式生成cookie后,在下次登录时,Spring Security首先用Base64简单解码得到用户名、过期时间和加密散列值;然后使用用户名得到密码;接着重新以该散列算法正向计算,并将计算结果与旧的加密散列值进行对比,从而确认该令牌是否有效。
5.2、实现自动登录
1、散列加密方案
在Spring Security中加入自动登录的功能非常简单。
@Override protected void configure(HttpSecurity http) throws Exception { //所有页面都是授予所有权限 http.authorizeRequests() .antMatchers("/admin/api/**").hasRole("ADMIN") .antMatchers("/user/api/**").hasRole("USER") .antMatchers("/app/api/**").permitAll() .anyRequest().authenticated() //其他所有都需要授权才能访问 .and() .formLogin() .loginPage("/login").permitAll() //1、指定登录页面的URL .defaultSuccessUrl("/") //2、登录成功后的页面 .and().rememberMe().rememberMeParameter("rememberMe").userDetailsService(userDetailsService).and() .logout(); //3、退出功能 }
前提是已经实现了一个UserDetailsService。重启服务后访问受限API,这次在表单登录页中多了一个可选框,如图所示。
勾选“记住我”可选框,按照正常的流程登录,并在开发者工具中查看浏览器cookie,可以看到除JSESSIONID外多了一个值,如图所示。
这就是Spring Security默认自动登录的cookie字段。在不配置的情况下,过期时间是两个星期。
public abstract class AbstractRememberMeServices implements RememberMeServices, InitializingBean, LogoutHandler, MessageSourceAware { //两个星期 public static final int TWO_WEEKS_S = 1209600;
Spring Security会在每次表单登录成功之后更新此令牌,具体处理方式在源码中有体现。
总体来说,这种方式不需要服务器花费空间来存储自动登录的相关数据,实现简单,安全性相对较高。但存在潜在风险,即如果该令牌在有效期内被盗取,那么用户的身份将完全暴露。
2、持久化令牌方案
持久化令牌方案在交互上与散列加密方案一致,都是在用户勾选Remember-me之后,将生成的令牌发送到用户浏览器,并在用户下次访问系统时读取该令牌进行认证。不同的是,它采用了更加严谨的安全性设计。
在持久化令牌方案中,最核心的是series和token两个值,它们都是用MD5散列过的随机字符串。不同的是,series仅在用户使用密码重新登录时更新,而token会在每一个新的session中都重新生成。
这样设计有什么好处呢?
首先,解决了散列加密方案中一个令牌可以同时在多端登录的问题。每个会话都会引发token的更新,即每个token仅支持单实例登录。
其次,自动登录不会导致series变更,而每次自动登录都需要同时验证series和token两个值,当该令牌还未使用过自动登录就被盗取时,系统会在非法用户验证通过后刷新token 值,此时在合法用户的浏览器中,该token值已经失效。当合法用户使用自动登录时,由于该series对应的token 不同,系统可以推断该令牌可能已被盗用,从而做一些处理。例如,清理该用户的所有自动登录令牌,并通知该用户可能已被盗号等。