1。RememberMe简介RememberMe是一个非常常用的功能。图6-1为登录QQ邮箱时的“记住我”选项。说到RememberMe,一些初学者往往会有一些误解,认为RememberMe的作用是将用户名/密码用cookie保存在浏览器中,这样下次登录时就不用再输入用户名/密码了时间。这种理解显然是错误的。我们这里所说的RememberMe是一种服务端行为。传统的登录方式是基于Session会话的。一旦用户关闭浏览器再打开,又要重新登录,太麻烦了。如果有一种机制可以让用户在关闭和重新打开浏览器后继续保持认证状态,将会方便很多。RememberMe就是为解决这个需求而诞生的。具体实现思路是通过cookie记录当前用户身份。当用户登录成功后,用户信息、时间戳等会通过一定的算法进行加密。加密完成后会通过responseheader带回前端,保存在cookie中。当浏览器关闭并重新打开时,如果您再次访问该网站,cookie中的信息将自动发送到服务器,服务器将对cookie中的信息进行验证和分析,以确定用户的身份。cookie中保存的用户信息也是有时间限制的,比如三天、一周等,细心的读者可能已经发现,这种做法存在安全隐患。所谓鱼和熊掌不可兼得。想要使用方便,就不得不牺牲一定的安全性。但是,在本章中,我们将介绍使用持久性令牌和二次验证来降低使用RememberMe带来的安全性。风险。2.RememberMe的基本用法先来看最简单的用法。首先创建一个SpringBoot项目,引入spring-boot-starter-security依赖。项目创建成功后,添加一个HelloController并创建一个测试界面,代码如下:@RestControllerpublicclassHelloController{@GetMapping("/hello")publicStringhello(){return"hello";}}然后创建一个SecurityConfig配置文件:}@Overrideprotectedvoidconfigure(AuthenticationManagerBuilderauth)throwsException{auth.inMemoryAuthentication().withUser("javaboy").password("123").roles("admin");}@Overrideprotectedvoidconfigure(HttpSecurityhttp)throwsException{http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().rememberMe().key("javaboy").and().csrf().disable();}}这里主要是调用HttpSecurity中的rememberMe方法,配置一个key,最终会将RememberMeAuthenticationFilter添加到过滤器链中。过滤器配置完成,最后开始项目,当我们访问/hello接口时,会自动跳转到登录页面,如图6-2所示,可以看到默认登录页面现在多了一个RememberMe选项,勾选RememberMe,登录后为成功,我们就可以访问/hello界面了,访问完成后,关闭浏览器再重新打开,此时可以直接访问/hello界面,无需登录;同时,如果关闭服务器并重新打开,然后进入/hello界面,发现此时不需要登录。那么这一切是如何实现的呢?打开浏览器控制台,我们来分析一下整个登录过程。首先,当我们点击登录按钮,有一个额外的请求参数remember-me,如图6-3所示。显然,remember-me参数是用来告诉服务器是否开启RememberMe功能的。如果开发者自定义登录页面,那么默认是否开启RememberMe参数是remember-me。请求成功后,在响应头中添加一个Set-Cookie,如图6-4所示。记住我的字符串在响应头中给出。以后所有请求的请求头cookie字段都会自动携带这个token,服务器可以通过这个token来验证用户身份是否合法。大致流程是这样的,但是大家发现这种方式存在很大的安全隐患。一旦remember-metoken泄露,恶意用户就可以利用这个token随意访问系统资源。持久化令牌和二次验证可以在一定程度上降低这个问题的风险。3.Persistenttokens使用persistenttoken实现RememberMe的体验和使用普通token的登录体验是一样的,不同的是服务端做了什么改变了。持久化令牌在普通令牌的基础上,增加了系列和令牌两个验证参数。使用用户名/密码登录时,系列会自动更新;一旦有新的会话,令牌将被重新生成。因此,如果token被盗,一旦对方基于RememberMe登录成功,就会生成一个新的token,而你自己的登录token将失效,以便及时发现账号泄露并进行处理,这样如清除自动登录token卡,通知用户账号泄露等。SpringSecurity为持久化token提供了两种实现:JdbcTokenRepositoryImplInMemoryTokenRepositoryImpl前者基于JdbcTemplate操作数据库,后者操作存储在内存中的数据。由于InMemoryTokenRepositoryImpl的使用场景较少,这里主要介绍基于JdbcTokenRepositoryImpl的配置。首先创建一个security06数据库,然后我们需要一个表来记录token信息。用于创建表的SQL脚本已在JdbcTokenRepositoryImpl类中的CREATE_TABLE_SQL变量上定义。代码如下:publicstaticfinalStringCREATE_TABLE_SQL="createtablepersistent_logins(usernamevarchar(64)notnull,seriesvarchar(64)primarykey,"+"tokenvarchar(64)notnull,last_usedtimestampnotnull)";我们直接将变量中定义的SQL脚本复制到数据库中执行,生成一个persistent_logins表来记录token信息。persistent_logins表一共有四个字段:username表示登录用户名,series表示生成的series字符串,token表示生成的token字符串,last_used表示上次使用时间。接下来在项目中引入JdbcTemplate依赖和MySQL数据库驱动依赖: