`

spring-security(二十三)Remember-Me认证

阅读更多
前言:
  Remember-me认证方式指的是能在不同的会话间记录用户认证信息的功能。通常通过向客户端发送一个cookie,在以后用户访问网站时通过这个cookie中的信息来自动登录实现。spring security 已经为我们提供了必要的hooks实现这个功能,并提供了两种具体的实现。一种是cookie中token包含了所有认证所需信息,并通过hash算法来保护这个token,另一种通过数据库或者其他持久化机制来存储生成的token。两种实现方式都需要我们提供一个UserDetailsService。如果我们采取的认证方式没有用到UserDetailsService(如LDAP),除非我们在application context中创建一个UserDetailsService来适配,否则Remember-me功能将不能正确执行。

一、remember-me功能实现逻辑
  • 用户首次登录认证成功后,调用RememberMeServices的loginSuccess方法,将用户凭证信息序列化成token,以某种形式存储起来
  • 用户session失效后再次访问系统,会调用RememberMeServices的autoLogin方法,根据客户端传过来的cookie信息,获取之前存储的token信息,并解析出认证需要的信息,完成自动认证

二.基于简单hash的方式
1.这种方式MD5算法实现remember-me功能,核心思想是在用户认证成功后通过下面的算法生成一个token,token中包含用户名和密码等信息,并将此token缓存到客户端的cookie中。
base64(username + ":" + expirationTime + ":" +
md5Hex(username + ":" + expirationTime + ":" password + ":" + key))
username: 用户名
password:  密码
expirationTime: 过期日期,单位是毫秒,默认两周
key: 一个私有的key值,用来保护token,防止被伪造

通过上面的算法得到的token,如果在有效期内,并且用户名和密码没有变化的情况下就可以实现自动认证功能,过程如下
  • 服务器接收到token后,利用base64对token值解码,获取到username、expirationTime、以及一个特定的hash串,hash串中包含了用户的密码信息(因为hash算法的不可逆性,这个密码是不能恢复出来的)
  • 服务器判断expirationTime有没有过期,如果过期就抛出异常不再进行自动认证
  • 服务器根据解析出来的username,调用UserDetailsService.loadUserByUsername方法获取用户对应的密码、权限等信息
  • 将username、获取出来的password、expirationTime、key值经过相同的hash算法得到一个hash串
  • 判断上一步计算出来的hash串和我们从cookie中解析出来的hash串是否一致,一致的话生成一个RememberMeAuthenticationToken,不一致抛出异常不在进行自动认证
  • 接着调用authenticationManager.authenticate,传入上一步生成的token
  • authenticationManager中最终会调用到RememberMeAuthenticationProvider的authenticate方法,因为在之前我们已经判断过现在的cookie中存储的认证信息对应的用户名和密码是正确的,所以这个方法只是简单的判断下RememberMeAuthenticationToken中的key值是否和RememberMeAuthenticationProvider中设定的key一致就可以了
  • 如果key值一致则认证成功,并将认证后的Authentication存入SecurityContextHolder供鉴权使用

2.采用这种方式有一个潜在安全问题,就是一个缓存下来的token可以被任何用户使用,一直到设定的时效过期,并且在这个过程中用户本人并不能察觉到,在摘要认证中也有同样的问题。如果非法的使用者做了某种操作使用户发现remember-me token被泄漏了,用户可以通过修改密码来使所有缓存的token失效。另一个安全问题是我们需要把username也放到cookie中,也会造成一定的信息泄露。如果安全级别比较高,应该采用下面讲的第二种方式,将token存入到库中,否则应该禁止使用remember-me功能。
3.在spring boot中通过下面的配置启用
           http
		.csrf()
			.disable()
		.authorizeRequests()
			.anyRequest().authenticated()
			.and()
		.formLogin()
			.loginPage("/login.html")
			.permitAll()
			.and()
		.rememberMe()
			.and()
		.logout()
			.logoutSuccessUrl("/login.html");

spring boot会自动给我们装配一个UserDetailsService,如果我们系统中有超过一个的UserDetailsService,我们必须明确的通过rememberMe().userDetailsService(userDetailsService)方法指定一个。
具体例子可参考spring-security(五)java config-sample之rememberme
4.基于hash的remember-me功能策略类是TokenBasedRememberMeServices
这个类还实现了LogoutHandler接口,当在用户logout时要删除token的需求时,可以将这个类配置到LogoutFilter中。
三、采用持久化机制实现
1.这种实现方式的核心算法是首次登录成功后生成一个PersistentRememberMeToken对象,包含如下信息
  • username 用户名
  • series 一个16位的随机数,之后会把这个数写入客户端的cookie中,通过这个值能唯一确定一条token记录
  • tokenValue 一个16位的随机数,代表着当前的token值
  • date 当前系统时间,即用户访问系统认证成功的时间

接着将生成的PersistentRememberMeToken存入到PersistentTokenRepository中,spring默认给我们提供了
[list]
  • 两种实现InMemoryTokenRepositoryImpl和JdbcTokenRepositoryImpl,其中InMemoryTokenRepositoryImpl中就是一个Map<String, PersistentRememberMeToken> seriesTokens,key值对应的就是series
  • JdbcTokenRepositoryImpl会将我们的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)

    主键是series
    [/list]
    然后将series、tokenValue缓存到客户端的cookie中
    2.当用户session过期后用户重新访问系统时通过以下过程实现认证
    • 从cookie中解析出series、tokenValue
    • 根据series从PersistentTokenRepository中获取到PersistentRememberMeToken
    • 用获取到的PersistentRememberMeToken中的tokenValue和cookie中获取到的tokenValue进行比较看是否匹配,不匹配的话,很有可能是token泄露了,此时spring会将此用户对应的所有token都清除掉,并抛出异常自动认证结束
    • 根据PersistentRememberMeToken中的last_used时间+设置的token有效期和当前系统时间相比判读是否过期,过期抛出异常自动认证结束
    • 利用原来的token的series,并重新生成一个tokenValue,再构造一个PersistentRememberMeToken,利用series为key将原来的更新掉,并将last_used更新成当前系统时间
    • 再将series、和新生成的tokenValue缓存到客户端的cookie中
    • 接着调用UserDetailsService.loadUserByUsername方法获取用户对应的密码、权限等信息
    • 用获取到的user信息创建一个RememberMeAuthenticationToken
    • 接着调用authenticationManager.authenticate,传入上一步生成的token
    • authenticationManager中最终会调用到RememberMeAuthenticationProvider的authenticate方法,因为在之前我们已经判断过现在的cookie中存储的认证信息对应的用户名和密码是正确的,所以这个方法只是简单的判断下RememberMeAuthenticationToken中的key值是否和RememberMeAuthenticationProvider中设定的key一致就可以了
    • 如果key值一致则认证成功,并将认证后的Authentication存入SecurityContextHolder供鉴权使用

    3.具体实现类PersistentTokenBasedRememberMeServices,这个类也实现了LogoutHandler,当用户执行logout时,这个类会自动将当前用户的所有PersistentRememberMeToken删除
    @Override
    public void logout(HttpServletRequest request, HttpServletResponse response,
    		Authentication authentication) {
    	super.logout(request, response, authentication);
    	if (authentication != null) {
    		tokenRepository.removeUserTokens(authentication.getName());
    	}
    }
    


    4.由上面的分析可知采用这种形式即可以避免将用户名称返回客户端,并且只要用户点击了logout,就可以将所有缓存的PersistentRememberMeToken都删除,可以避免因为不小心忘记登出而让其他的非法使用者一直利用自己的账号信息的问题。
    四、在RememberMeAuthenticationProvider中进行认证时,需要判断key值是否相同,那这个key是怎么设置的,如何和RememberMeServices中的key保持一致呢?
    在spring boot的Java config中,我们通过http.rememberme()来配置remember-me功能,下面来看下这个函数
    public RememberMeConfigurer<HttpSecurity> rememberMe() throws Exception {
        return getOrApply(new RememberMeConfigurer<HttpSecurity>());
    }
    

    具体的配置在RememberMeConfigurer中
    public void init(H http) throws Exception {
        validateInput();
        String key = getKey();----------------------------------------------------(1)
        RememberMeServices rememberMeServices = getRememberMeServices(http, key);-(2)
        http.setSharedObject(RememberMeServices.class, rememberMeServices);
        LogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);
        if (logoutConfigurer != null && this.logoutHandler != null) {
    		logoutConfigurer.addLogoutHandler(this.logoutHandler);
    	}
        RememberMeAuthenticationProvider authenticationProvider = new RememberMeAuthenticationProvider(key);----------------------------------------(3)
        authenticationProvider = postProcess(authenticationProvider);
        http.authenticationProvider(authenticationProvider);
        initDefaultLoginFilter(http);
    	}
    

    可以看出,在这个配置类里面给我们创建了RememberMeServices和RememberMeAuthenticationProvider并且用的是同一个key值

    分享到:
    评论

    相关推荐

      spring-security源代码

      Spring Security提供Remember Me服务,可以让用户在一段时间内免登录访问,通过在cookie中存储安全的凭据实现。 5. **CSRF防护**: 默认情况下,Spring Security开启CSRF防护,通过生成并验证CSRF令牌,防止跨站...

      spring-security-core-3.1.0.RC1.jar

      二、`spring-security-core-3.1.0.RC1.jar`解析 1. **核心组件**:此jar包包含了Spring Security框架的基本组件,如Authentication(认证)、Authorization(授权)和Access Control(访问控制)机制。其中,...

      spring-security-3.1.4

      Spring Security 是一个强大的和高度可定制的身份验证和访问控制框架,用于Java应用程序。这个"spring-security-3.1.4"版本是该框架的一个稳定发行版,提供了在2012年的安全解决方案。这个zip包包含了Spring ...

      spring-security-core-2.0.5.RELEASE.src

      - 对RememberMe服务进行了改进,增强了安全性。 总结 Spring Security通过`spring-security-core`模块提供了全面的安全管理能力,它将认证、授权、会话管理和安全配置等抽象成易于使用的组件。深入理解这个源代码...

      spring-security-4.0.3.RELEASE-全包

      2. **Authentication**:Spring Security提供了一套完整的身份验证机制,包括Remember Me服务、基于表单的登录、以及支持各种认证源如LDAP、数据库等。4.0.3版本可能包含了改进的认证流程和错误处理。 3. **...

      spring-security cookie认证

      Spring Security 是一个强大的安全框架,用于Java和Spring应用程序。它为Web应用提供了全面的安全解决...在实际开发中,结合文件"springsecurity"中的示例代码,可以更好地理解和实践Spring Security的Cookie认证功能。

      spring-security 官方文档 中文版

      - **Remember-Me 认证**:允许用户在关闭浏览器后仍然保持登录状态。 - **添加 HTTP/HTTPS 信道安全**:通过配置 HTTPS 来保障数据传输的安全。 - **会话管理**: - **检测超时**:设置会话超时时间。 - **同步...

      spring-security-3.0.5.RELEASE 官方下载

      10. **OAuth支持**:尽管可能不是3.0.5.RELEASE的核心功能,但Spring Security可能已经具备了与OAuth协议集成的基础,允许第三方应用访问受保护的资源。 要深入了解Spring Security 3.0.5.RELEASE,你可以查看官方...

      spring-security文档和jar包

      1. **认证**:通过提供不同类型的Authentication Provider,如数据库、LDAP、Remember Me服务等,Spring Security实现了灵活的用户身份验证。 2. **授权**:基于Role的访问控制(RBAC)允许开发者定义角色和权限,...

      spring-security-3.1.0.RELEASE

      3.1.0.RELEASE版本引入了RememberMe服务,允许用户在一段时间内无需重新登录。 2. **授权**:基于角色的访问控制(RBAC)是Spring Security授权的基础,通过配置访问决策管理器(AccessDecisionManager)和访问决策...

      spring-security-3.1.0.RELEASE.zip

      - **Remember Me**:提供一种机制,允许用户在一段时间内无须重新登录。 3. **授权与访问控制** - **Role-Based Access Control (RBAC)**: 基于角色的访问控制,用户被分配角色,角色对应权限。 - **Expression-...

      spring-security-4.0 jar包

      Spring Security提供了Remember Me服务,允许用户在关闭浏览器后仍然保持登录状态。它通过存储一个长期的cookie来实现这一功能。 8. **国际化支持**: Spring Security 4.0支持多语言环境,可以方便地配置不同...

      spring-security-taglibs-2.0.4.jar.zip

      4. `&lt;sec:authentication-properties&gt;`:这个标签允许开发者自定义与认证相关的属性,例如记住我(remember-me)功能。 5. `&lt;sec:csrf&gt;`:跨站请求伪造(CSRF)防护标签,防止恶意用户在用户浏览器中执行非预期的...

      Spring-Security_java_

      6. **Remember Me**: Spring Security 提供了“记住我”功能,允许用户在一段时间内无需重新登录。这个功能通过在用户的浏览器中设置长期 cookie 来实现。 7. **Session Management**: 该组件管理用户会话,防止...

      spring-security-3.2.9和spring-framework-3.2.4的jar包和源码

      3. **认证**:Spring Security提供了多种认证机制,如基于表单的登录、基于令牌的认证(如JWT)等。 4. **授权**:通过访问决策管理器和访问决策投票器来决定用户是否被授权访问特定资源。 5. **表达式式访问控制**...

      spring-security-2.0.3

      6. **记住我功能**:Spring Security允许用户选择在关闭浏览器后仍然保持登录状态,通过“Remember Me”服务实现。在2.0.3中,可以学习如何配置和安全地实现这一特性。 7. **异常处理**:Spring Security有自己的...

      spring-security简单demo

      8. **记住我(Remember Me)**:Spring Security提供了"记住我"功能,允许用户在关闭浏览器后仍能保持登录状态。这通常涉及到一个持久化的Token存储,如数据库。 这个"spring-security简单demo"应该涵盖以上这些...

      SpringSecurity3.1.3的jar包

      1. **spring-security-core-3.1.3.RELEASE.jar**:这是Spring Security的核心模块,它提供了基本的安全抽象,如安全上下文、权限表达式、访问决策管理器和认证管理器。这个模块实现了基于角色的访问控制(RBAC),并...

      spring-security demo

      在这个项目中,我们可以深入学习如何使用Spring Security来实现用户认证、权限控制以及安全相关的功能。 首先,Spring Security的核心组件包括: 1. **Filter Security Chain**: 这是一系列的过滤器,它们在HTTP...

    Global site tag (gtag.js) - Google Analytics