`

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-3.0.1中文官方文档(翻译版)

      2.3.1. Remember-Me 认证 2.3.2. 添加HTTP/HTTPS 信道安全 2.3.3. 会话管理 2.3.3.1. 检测超时 2.3.3.2. 同步会话控制 2.3.3.3. 防止Session 固定攻击 2.3.4. 对OpenID 的支持 2.3.4.1. 属性交换 ...

      spring security 参考手册中文版

      9.3.1什么是Spring Security中的认证? 78 9.3.2直接设置SecurityContextHolder内容 80 9.4 Web应用程序中的身份验证 81 9.4.1 ExceptionTranslationFilter 82 9.4.2 AuthenticationEntryPoint 82 9.4.3认证机制 82 ...

      Spring Security 中文教程.pdf

      2.3.1. Remember-Me认证 2.3.2. 添加HTTP/HTTPS信道安全 2.3.3. 会话管理 2.3.3.1. 检测超时 2.3.3.2. 同步会话控制 2.3.3.3. 防止Session固定攻击 2.3.4. 对OpenID的支持 2.3.4.1. 属性交换 2.3.5....

      Spring Security实战源码

      该资源是基本Spring Security实战七篇文档中组织的源码,详情...ssecurity-rememberMe项目是Spring Security实战(七)的源码; 本人开发工具是IDEA,每个项目中的代码均可以运行并测试。Eclipse也是一样可以运行的。

      Spring Security3.2搭建LDAP认证授权

      进阶-使用Spring Security3.2搭建LDAP认证授权和Remember-me

      SpringSecurity 3.0.1.RELEASE.CHM

      2.3.1. Remember-Me认证 2.3.2. 添加HTTP/HTTPS信道安全 2.3.3. 会话管理 2.3.3.1. 检测超时 2.3.3.2. 同步会话控制 2.3.3.3. 防止Session固定攻击 2.3.4. 对OpenID的支持 2.3.4.1. 属性交换 2.3.5. 添加你...

      spring security 3.0x remember-me 免登陆

      基于用户,角色,权限的spring_security完整项目 博文链接:https://abc08010051.iteye.com/blog/1995648

      Spring Security 构建rest服务实现rememberme 记住我功能

      主要介绍了Spring Security 构建rest服务实现rememberme 记住我功能,需要的朋友可以参考下

      spring boot +spring security+thymeleaf实现权限

      spring boot +spring security +thymeleaf 实现简单权限+remember-me功能

      Spring Security学习之rememberMe自动登录的实现

      主要给大家介绍了关于Spring Security学习之rememberMe自动登录的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

      springboot-security-demo4

      用户登录(使用RememberMe) 使用简单(单个)角色 方法级别的安全性(@PreAuthorize) XSS和CSRF测试 下一个 用户认证管理 参考 快速开始 预装 JDK 1.8(或Java 10) Maven的3.5 吉特 跑步 git clone ...

      Spring Security 教程(Spring Security Tutorial)1

      摘要认证的密码加密通用密码加密Remember-Me(记住我)认证:基于散列的令牌方法Remember-Me(记住我)认证:基于持久化的令牌方法OAuth 2.

      Spring Security Remember me使用及原理详解

      主要介绍了Spring Security Remember me使用及原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

      springsecurity3.0.5

      springsecurity3.0.5简单例子 当用firefox访问时产生的临时cookie,在下次访问中还存在的话,获取的session还是原来的session(user验证信息不变),如果把那个临时cookie删除的话,则springsecuriyt生成新的session...

      SpringSecurity rememberme功能实现过程解析

      主要介绍了SpringSecurity rememberme功能实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

      《Spring Security3》第四章第四部分翻译(Remember me后台存储和SSL)附前四章doc文件

      NULL 博文链接:https://lengyun3566.iteye.com/blog/1141347

      SpringAll_wuyouzhuguli.tar.gz

      Spring Boot Shiro Remember Me Spring Boot Shiro权限控制 Spring Boot Shiro Redis Spring Boot Shiro Ehcache Spring Boot Thymeleaf中使用Shiro标签 Spring Boot Shiro在线会话管理 Spring Boot Shiro整合JWT 三...

      liceyo-study-security:Spring Boot Security 学习,实现了些简单的东西

      Spring Security 学习 使用Spring Boot 2.0 为框架,学习Spring Security。 已完成功能: ...实现RememberMe功能。 使用Redis记录Spring Session。 简单完成各种情况下从Ajax请求跳转到页面的处理。

      4、Spring Security 安全权限管理手册

      实现免登陆验证(Remember-Me验证) 提供一系列标签库进行页面元素的安全控制 方法安全 通过AOP模式实现安全代理 Web安全与方法安全均可以使用表达式语言定义访问规则 5、配置SS 配置Web.xml,应用安全过滤器 配置...

      SpringSecurity function ModifyAuthroties RemebeMe

      适合的版本才是最好的,最新的可能有些不稳定; 之前遇到过 druid 启动器版本的问题; 事实证明追求最新版本会有些许问题,就像majora等linux一样; 稳定才是我们需要的

    Global site tag (gtag.js) - Google Analytics