上一篇:【认证、授权攻略三(9)、spring security匿名登录】
remember me 功能
概述
Remember-Me 是指网站能够在 Session 之间记住登录用户的身份,具体来说就是我成功认证一次之后在一定的时间内我可以不用再输入用户名和密码进行登录了,系统会自动给我登录。这通常是通过服务端发送一个 cookie 给客户端浏览器,下次浏览器再访问服务端时服务端能够自动检测客户端的 cookie,根据 cookie 值触发自动登录操作。Spring Security 为这些操作的发生提供必要的钩子,并且针对于 Remember-Me 功能有两种实现。
一种是简单的使用加密来保证基于 cookie 的 token 的安全,
另一种是通过数据库或其它持久化存储机制来保存生成的 token。
需要注意的是两种实现都需要一个 UserDetailsService。如果你使用的 AuthenticationProvider 不使用 UserDetailsService,那么记住我将会不起作用,除非在你的 ApplicationContext 中拥有一个 UserDetailsService 类型的 bean。
基于简单加密 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 功能总是有点不安全的。
使用这种方式时,我们只需要在 http 元素下定义一个 remember-me 元素,同时指定其 key 属性即可。key 属性是用来标记存放 token 的 cookie 的,对应上文提到的生成 token 时的那个 key。
<security:http auto-config="true">
<security:form-login/>
<!-- 定义记住我功能 -->
<security:remember-me key="elim"/>
<security:intercept-url pattern="/**" access="ROLE_USER" />
</security:http>
这里有两个需要注意的地方。第一,如果你的登录页面是自定义的,那么需要在登录页面上新增一个名为 “remember-me” 的 checkbox,这是基于 NameSpace 定义提供的默认名称,如果要自定义可以自己定义 TokenBasedRememberMeServices 或 PersistentTokenBasedRememberMeServices 对应的 bean,然后通过其 parameter 属性进行指定,具体操作请参考后文关于《Remember-Me 相关接口和实现类》部分内容。第二,上述功能需要一个 UserDetailsService,如果在你的 ApplicationContext 中已经拥有一个了,那么 Spring Security 将自动获取;如果没有,那么当然你需要定义一个;如果拥有在 ApplicationContext 中拥有多个 UserDetailsService 定义,那么你需要通过 remember-me 元素的 user-service-ref 属性指定将要使用的那个。如:
<security:http auto-config="true">
<security:form-login/>
<!-- 定义记住我功能,通过 user-service-ref 指定将要使用的 UserDetailsService-->
<security:remember-me key="elim" user-service-ref="userDetailsService"/>
<security:intercept-url pattern="/**" access="ROLE_USER" />
</security:http>
<bean id="userDetailsService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
基于持久化 token 的方法
持久化 token 的方法跟简单加密 token 的方法在实现 Remember-Me 功能上大体相同,都是在用户选择了 “记住我” 成功登录后,将生成的 token 存入 cookie 中并发送到客户端浏览器,待到下次用户访问系统时,系统将直接从客户端 cookie 中读取 token 进行认证。所不同的是基于简单加密 token 的方法,一旦用户登录成功后,生成的 token 将在客户端保存一段时间,如果用户不点击退出登录,或者不修改密码,那么在 cookie 失效之前,他都可以使用该 token 进行登录,哪怕该 token 被别人盗用了,用户与盗用者都同样可以进行登录。而基于持久化 token 的方法采用这样的实现逻辑:
用户选择了 “记住我” 成功登录后,将会把 username、随机产生的序列号、生成的 token 存入一个数据库表中,同时将它们的组合生成一个 cookie 发送给客户端浏览器。
当下一次没有登录的用户访问系统时,首先检查 cookie,如果对应 cookie 中包含的 username、序列号和 token 与数据库中保存的一致,则表示其通过验证,系统将重新生成一个新的 token 替换数据库中对应组合的旧 token,序列号保持不变,同时删除旧的 cookie,重新生成包含新生成的 token,就的序列号和 username 的