`
234390216
  • 浏览: 10233012 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
博客专栏
A5ee55b9-a463-3d09-9c78-0c0cf33198cd
Oracle基础
浏览量:462624
Ad26f909-6440-35a9-b4e9-9aea825bd38e
springMVC介绍
浏览量:1775519
Ce363057-ae4d-3ee1-bb46-e7b51a722a4b
Mybatis简介
浏览量:1398366
Bdeb91ad-cf8a-3fe9-942a-3710073b4000
Spring整合JMS
浏览量:395022
5cbbde67-7cd5-313c-95c2-4185389601e7
Ehcache简介
浏览量:679984
Cc1c0708-ccc2-3d20-ba47-d40e04440682
Cas简介
浏览量:530892
51592fc3-854c-34f4-9eff-cb82d993ab3a
Spring Securi...
浏览量:1183947
23e1c30e-ef8c-3702-aa3c-e83277ffca91
Spring基础知识
浏览量:467932
4af1c81c-eb9d-365f-b759-07685a32156e
Spring Aop介绍
浏览量:151396
2f926891-9e7a-3ce2-a074-3acb2aaf2584
JAXB简介
浏览量:68153
社区版块
存档分类
最新评论
阅读更多

Remember-Me功能

 

目录

 

1.1     概述

1.2     基于简单加密token的方法

1.3     基于持久化token的方法

1.4     Remember-Me相关接口和实现类

1.4.1    TokenBasedRememberMeServices

1.4.2    PersistentTokenBasedRememberMeServices

 

1.1          概述

       Remember-Me是指网站能够在Session之间记住登录用户的身份,具体来说就是我成功认证一次之后在一定的时间内我可以不用再输入用户名和密码进行登录了,系统会自动给我登录。这通常是通过服务端发送一个cookie给客户端浏览器,下次浏览器再访问服务端时服务端能够自动检测客户端的cookie,根据cookie值触发自动登录操作。Spring Security为这些操作的发生提供必要的钩子,并且针对于Remember-Me功能有两种实现。一种是简单的使用加密来保证基于cookietoken的安全,另一种是通过数据库或其它持久化存储机制来保存生成的token

       需要注意的是两种实现都需要一个UserDetailsService。如果你使用的AuthenticationProvider不使用UserDetailsService,那么记住我将会不起作用,除非在你的ApplicationContext中拥有一个UserDetailsService类型的bean

 

1.2          基于简单加密token的方法

       当用户选择了记住我成功登录后,Spring Security将会生成一个cookie发送给客户端浏览器。cookie值由如下方式组成:

base64(username+":"+expirationTime+":"+md5Hex(username+":"+expirationTime+":"+password+":"+key))

Ø  username:登录的用户名。

Ø  password:登录的密码。

Ø  expirationTimetoken失效的日期和时间,以毫秒表示。

Ø  key:用来防止修改token的一个key

       这样用来实现Remember-Me功能的token只能在指定的时间内有效,且必须保证token中所包含的usernamepasswordkey没有被改变才行。需要注意的是,这样做其实是存在安全隐患的,那就是在用户获取到实现记住我功能的token后,任何用户都可以在该token过期之前通过该token进行自动登录。如果用户发现自己的token被盗用了,那么他可以通过改变自己的登录密码来立即使其所有的记住我token失效。如果希望我们的应用能够更安全一点,可以使用接下来要介绍的持久化token方式,或者不使用Remember-Me功能,因为Remember-Me功能总是有点不安全的。

       使用这种方式时,我们只需要在http元素下定义一个remember-me元素,同时指定其key属性即可。key属性是用来标记存放tokencookie的,对应上文提到的生成token时的那个key

   <security:http auto-config="true">

      <security:form-login/>

      <!-- 定义记住我功能 -->

      <security:remember-me key="elim"/>

      <security:intercept-url pattern="/**" access="ROLE_USER" />

   </security:http>

 

       这里有两个需要注意的地方。第一,如果你的登录页面是自定义的,那么需要在登录页面上新增一个名为“_spring_security_remember_me”的checkbox,这是基于NameSpace定义提供的默认名称,如果要自定义可以自己定义TokenBasedRememberMeServicesPersistentTokenBasedRememberMeServices对应的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>

 

1.3          基于持久化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,就的序列号和usernamecookie发送给客户端。

       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功能了。

    使用持久化token方法时需要我们的数据库中拥有如下表及其表结构。

create table persistent_logins (username varchar(64) not null,

                                    series varchar(64) primary key,

                                    token varchar(64) not null,

                                    last_used timestamp not null)

 

    然后还是通过remember-me元素来使用,只是这个时候我们需要其data-source-ref属性指定对应的数据源,同时别忘了它也同样需要ApplicationContext中拥有UserDetailsService,如果拥有多个,请使用user-service-ref属性指定remember-me使用的是哪一个。

   <security:http auto-config="true">

      <security:form-login/>

      <!-- 定义记住我功能 -->

      <security:remember-me data-source-ref="dataSource"/>

      <security:intercept-url pattern="/**" access="ROLE_USER" />

   </security:http>

 

1.4     Remember-Me相关接口和实现类

在上述介绍中,我们实现Remember-Me功能是通过Spring Security为了简化Remember-Me而提供的NameSpace进行定义的。而底层实际上还是通过RememberMeServicesUsernamePasswordAuthenticationFilterRememberMeAuthenticationFilter的协作来完成的。RememberMeServicesSpring SecurityRemember-Me提供的一个服务接口,其定义如下。

publicinterface RememberMeServices {

    /**

     * 自动登录。在实现这个方法的时候应该判断用户提供的Remember-Me cookie是否有效,如果无效,应当直接忽略。

     * 如果认证成功应当返回一个AuthenticationToken,推荐返回RememberMeAuthenticationToken

     * 如果认证不成功应当返回null

     */

    Authentication autoLogin(HttpServletRequest request, HttpServletResponse response);

    /**

     * 在用户登录失败时调用。实现者应当做一些类似于删除cookie之类的处理。

     */

    void loginFail(HttpServletRequest request, HttpServletResponse response);

    /**

     * 在用户成功登录后调用。实现者可以在这里判断用户是否选择了“Remember-Me”登录,然后做相应的处理。

     */

    void loginSuccess(HttpServletRequest request, HttpServletResponse response,

        Authentication successfulAuthentication);

}

 

       UsernamePasswordAuthenticationFilter拥有一个RememberMeServices的引用,默认是一个空实现的NullRememberMeServices,而实际当我们通过remember-me定义启用Remember-Me时,它会是一个具体的实现。用户的请求会先通过UsernamePasswordAuthenticationFilter,如认证成功会调用RememberMeServicesloginSuccess()方法,否则调用RememberMeServicesloginFail()方法。UsernamePasswordAuthenticationFilter是不会调用RememberMeServicesautoLogin()方法进行自动登录的。之后运行到RememberMeAuthenticationFilter时如果检测到还没有登录,那么RememberMeAuthenticationFilter会尝试着调用所包含的RememberMeServicesautoLogin()方法进行自动登录。关于RememberMeServices Spring Security已经为我们提供了两种实现,分别对应于前文提到的基于简单加密token和基于持久化token的方法。

 

1.4.1   TokenBasedRememberMeServices

       TokenBasedRememberMeServices对应于前文介绍的使用namespace时基于简单加密token的实现。TokenBasedRememberMeServices会在用户选择了记住我成功登录后,生成一个包含token信息的cookie发送到客户端;如果用户登录失败则会删除客户端保存的实现Remember-Mecookie。需要自动登录时,它会判断cookie中所包含的关于Remember-Me的信息是否与系统一致,一致则返回一个RememberMeAuthenticationTokenRememberMeAuthenticationProvider处理,不一致则会删除客户端的Remember-Me cookieTokenBasedRememberMeServices还实现了Spring SecurityLogoutHandler接口,所以它可以在用户退出登录时立即清除Remember-Me cookie

 

       如果把使用namespace定义Remember-Me改为直接定义RememberMeServices和对应的Filter来使用的话,那么我们可以如下定义。

   <security:http>

      <security:form-login login-page="/login.jsp"/>

      <security:intercept-url pattern="/login*.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>

      <security:intercept-url pattern="/**" access="ROLE_USER" />

      <!-- usernamePasswordAuthenticationFilter加入FilterChain -->

      <security:custom-filter ref="usernamePasswordAuthenticationFilter" before="FORM_LOGIN_FILTER"/>

      <security:custom-filter ref="rememberMeFilter" position="REMEMBER_ME_FILTER"/>

   </security:http>

   <!-- 用于认证的AuthenticationManager -->

   <security:authentication-manager alias="authenticationManager">

      <security:authentication-provider

         user-service-ref="userDetailsService"/>

      <security:authentication-provider ref="rememberMeAuthenticationProvider"/>

   </security:authentication-manager>

 

   <bean id="userDetailsService"

      class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">

      <property name="dataSource" ref="dataSource" />

   </bean>

 

   <bean id="usernamePasswordAuthenticationFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">

      <property name="rememberMeServices" ref="rememberMeServices"/>

      <property name="authenticationManager" ref="authenticationManager"/>

      <!-- 指定request中包含的用户名对应的参数名 -->

      <property name="usernameParameter" value="username"/>

      <property name="passwordParameter" value="password"/>

      <!-- 指定登录的提交地址 -->

      <property name="filterProcessesUrl" value="/login.do"/>

   </bean>

   <!-- Remember-Me对应的Filter -->

   <bean id="rememberMeFilter"

   class="org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">

      <property name="rememberMeServices" ref="rememberMeServices" />

      <property name="authenticationManager" ref="authenticationManager" />

   </bean>

   <!-- RememberMeServices的实现 -->

   <bean id="rememberMeServices"

   class="org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">

      <property name="userDetailsService" ref="userDetailsService" />

      <property name="key" value="elim" />

      <!-- 指定request中包含的用户是否选择了记住我的参数名 -->

      <property name="parameter" value="rememberMe"/>

   </bean>

   <!-- key值需与对应的RememberMeServices保持一致 -->

   <bean id="rememberMeAuthenticationProvider"

   class="org.springframework.security.authentication.RememberMeAuthenticationProvider">

      <property name="key" value="elim" />

   </bean>

 

       需要注意的是RememberMeAuthenticationProvider在认证RememberMeAuthenticationToken的时候是比较它们拥有的key是否相等,而RememberMeAuthenticationTokenkeyTokenBasedRememberMeServices提供的,所以在使用时需要保证RememberMeAuthenticationProviderTokenBasedRememberMeServiceskey属性值保持一致。需要配置UsernamePasswordAuthenticationFilterrememberMeServices为我们定义好的TokenBasedRememberMeServices,把RememberMeAuthenticationProvider加入AuthenticationManagerproviders列表,并添加RememberMeAuthenticationFilterUsernamePasswordAuthenticationFilterFilterChainProxy

 

1.4.2   PersistentTokenBasedRememberMeServices

       PersistentTokenBasedRememberMeServicesRememberMeServices基于前文提到的持久化token的方式实现的。具体实现逻辑跟前文介绍的以NameSpace的方式使用基于持久化tokenRemember-Me是一样的,这里就不再赘述了。此外,如果单独使用,其使用方式和上文描述的TokenBasedRememberMeServices是一样的,这里也不再赘述了。

       需要注意的是PersistentTokenBasedRememberMeServices是需要将token进行持久化的,所以我们必须为其指定存储tokenPersistentTokenRepositorySpring Security对此有两种实现,InMemoryTokenRepositoryImplJdbcTokenRepositoryImpl。前者是将token存放在内存中的,通常用于测试,而后者是将token存放在数据库中。PersistentTokenBasedRememberMeServices默认使用的是前者,我们可以通过其tokenRepository属性来指定使用的PersistentTokenRepository

       使用JdbcTokenRepositoryImpl时我们可以使用在前文提到的默认表结构。如果需要使用自定义的表,那么我们可以对JdbcTokenRepositoryImpl进行重写。定义JdbcTokenRepositoryImpl时需要指定一个数据源dataSource,同时可以通过设置参数createTableOnStartup的值来控制是否要在系统启动时创建对应的存入token的表,默认创建语句为“create table persistent_logins (username varchar(64) not null, series varchar(64) primary key, token varchar(64) not null, last_used timestamp not null)”,但是如果自动创建时对应的表已经存在于数据库中,则会抛出异常。createTableOnStartup属性默认为false

       直接显示地使用PersistentTokenBasedRememberMeServices和上文提到的直接显示地使用TokenBasedRememberMeServices的方式是一样的,我们只需要将上文提到的配置中RememberMeServices实现类TokenBasedRememberMeServices换成PersistentTokenBasedRememberMeServices即可。

   <!-- RememberMeServices的实现 -->

   <bean id="rememberMeServices"

   class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">

      <property name="userDetailsService" ref="userDetailsService" />

      <property name="key" value="elim" />

      <!-- 指定request中包含的用户是否选择了记住我的参数名 -->

      <property name="parameter" value="rememberMe"/>

      <!-- 指定PersistentTokenRepository -->

      <property name="tokenRepository">

         <bean class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl">

            <!-- 数据源 -->

            <property name="dataSource" ref="dataSource"/>

            <!-- 是否在系统启动时创建持久化token的数据库表 -->

            <property name="createTableOnStartup" value="false"/>

         </bean>

      </property>

   </bean>

 

(注:本文是基于Spring Security3.1.6所写)

(注:原创文章,转载请注明出处。原文地址:http://elim.iteye.com/blog/2163997

 

 

6
0
分享到:
评论
5 楼 18215361994 2016-05-18  
当下一次没有登录的用户访问系统时,首先检查cookie,如果对应cookie中包含的username、序列号和token与数据库中保存的一致,则表示其通过验证,系统将重新生成一个新的token替换数据库中对应组合的旧token,序列号保持不变,同时删除旧的cookie,重新生成包含新生成的token,就的序列号和username的cookie发送给客户端


---------------------------
这里咨询下:等于你每次登陆都会产生一个新的cookie,那如果用户目前属于登陆状态,然后你的cookie被别人也是可以拿去登陆的是吧?这样你怎么解决?因为你的序列号不变,然后只是改变token
4 楼 a87604476 2016-01-23  
楼主,我想请教下,这个token是怎么生成的?就是通过什么来生成的?比如用户名密码还是根据本地的一些环境配置
3 楼 234390216 2015-05-04  
effort0829 写道
加上这行
  <security:authentication-provider ref="rememberMeAuthenticationProvider"/>
有错误: 发现了以元素 'security:authentication-provider' 开头的无效内容

security是引入的namespace的前缀,具体可以参考本系列的第一篇文章。
2 楼 effort0829 2015-04-22  
加上这行
  <security:authentication-provider ref="rememberMeAuthenticationProvider"/>
有错误: 发现了以元素 'security:authentication-provider' 开头的无效内容
1 楼 liguanfeng 2015-01-05  
  • [list]
  • [*][list]
  • [*][*][list]
  • [*][*][*][list]
  • [*][*][*][*][list]
  • [*][*][*][*][*][list]
  • [*][*][*][*][*][*][list]
  • [*][*][*][*][*][*][*][list]
  • [*][*][*][*][*][*][*][*][list]
  • [*][*][*][*][*][*][*][*][*][list]
  • [*][*][*][*][*][*][*][*][*][*][list]
  • [*][*][*][*][*][*][*][*][*][*][*][list]
  • [*][*][*][*][*][*][*][*][*][*][*][*][list]
  • [*][*][*][*][*][*][*][*][*][*][*][*][*][img][/img]
  • [*][*][*][*][*][*][*][*][*][*][*][*]
  • [*][*][*][*][*][*][*][*][*][*][*][/list]
  • [*][*][*][*][*][*][*][*][*][*][/list]
  • [*][*][*][*][*][*][*][*][*][/list]
  • [*][*][*][*][*][*][*][*][/list]
  • [*][*][*][*][*][*][*][/list]
  • [*][*][*][*][*][*][/list]
  • [*][*][*][*][*][/list]
  • [*][*][*][*][/list]
  • [*][*][*][/list]
  • [*][*][/list]
  • [*][/list]
  • [/list]
  • [/list]

    相关推荐

      spring-security-core-3.1.0.RC1.jar

      在本篇文章中,我们将深入探讨Spring Security的核心库——`spring-security-core-3.1.0.RC1.jar`,以及它在安全领域的应用。 一、Spring Security简介 Spring Security是一款提供全面身份验证、授权和安全访问...

      Spring Security 资料合集

      这三份资料——"实战Spring Security 3.x.pdf"、"Spring Security 3.pdf" 和 "Spring Security使用手册.pdf" 将深入探讨这些概念,并提供实践指导,帮助读者掌握如何在实际项目中应用Spring Security。通过学习这些...

      SpringSecurity 3.0.1.RELEASE.CHM

      1.1. Spring Security是什么? 1.2. 历史 1.3. 发行版本号 1.4. 获得Spring Security 1.4.1. 项目模块 1.4.1.1. Core - spring-security-core.jar 1.4.1.2. Web - spring-security-web.jar 1.4.1.3. Config -...

      spring security4登陆例子

      本文将通过一个具体的示例——Spring MVC + Spring Security 4 的整合登录实例来深入探讨Spring Security的使用方法。 #### 二、核心配置类:`SecurityConfig` 在本例中,我们使用的是Java Config的方式进行Spring...

      SpringSecurity 中文

      通过以上章节的内容概述,我们可以看出Spring Security不仅提供了强大的安全防护功能,同时也考虑到了用户体验的重要性。开发者可以根据项目的实际需求灵活配置,从而构建出既安全又友好的应用程序。

      spring 的权限管理框架 中文参考手册

      - **Automatic "Remember-Me" Authentication**:设置一段时间内无需重复进行身份验证的功能。 - **Anonymous Authentication**:允许任何调用自动假定为特定的安全主体。 - **Run-As Authentication**:在同一会话...

      Spring in Action(第二版)中文版_part4-7

      12. **Spring Security的配置与使用**: 包括配置HTTP安全,使用Remember Me服务,处理异常,以及自定义认证和授权逻辑。 13. **Spring Boot与Spring Cloud**: 如果这部分包含Spring Boot和Spring Cloud的相关内容,...

      digipower-ureport.rar

      4. SpringSecurity的配置:配置认证和授权规则,如HTTP Basic认证、RememberMe功能等。 5. Controller层:处理HTTP请求,与Service层交互,提供API接口。 6. Service层:业务逻辑处理,调用DAO层进行数据操作。 7. ...

      Java高级技术之Shiro

      * 记住我(Remember Me):记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。 Shiro 的架构从外部来看,Shiro 由 Subject、SecurityManager、Realm 三部分组成。Subject 代表了当前“用户”,...

      Shopping-list

      《Spring Boot实战——基于"Shopping-list"项目的学习与探索》 Spring Boot作为现代Java开发的热门框架,以其简洁、高效和快速启动的特点深受开发者喜爱。本文将以一个名为"Shopping-list"的个人项目为实例,深入...

      MySpringSecurity

      - `rememberMe`:配置“记住我”功能,让用户在一定时间内无需再次登录。 五、实战应用与扩展 MySpringSecurity项目不仅可以作为学习Spring Security的基础,还可以作为模板应用于实际项目。通过调整配置和添加...

      一个简单的后台管理系统-基于Java SSM,Java Config配置+源代码+文档说明

      * RememberMe * 菜单信息在数据库配置,前台渲染成菜单列表 * more ## 3开发环境 * Java SSM(Java Config) * 权限Spring Security * 缓存Ehcache(后期加入) * 后台数据校验Hibernate Validation(后期加入) * more...

      通过过滤器管理用户权限(1)

      在实际项目中,`LoginFilter`可能会更复杂,可能需要考虑更多的细节,比如处理记住我(Remember Me)功能,处理跨站请求伪造(CSRF)攻击,或者与其他认证框架(如Spring Security)集成。 此外,`LoginFilter`通常...

    Global site tag (gtag.js) - Google Analytics