1. 概述

本文展示如何使用Spring Security配置启用和配置“记住我”(Remember Me)功能 。在前文中我们已经讨论过如何 搭建带有Security以及表单登录的MVC应用

该机制能跨session识别用户身份 ——所以首先要了解的是,"记住我"仅在session超时后才会介入。默认情况下,30分钟不活动会被认定为超时,但超时时间可以在web.xml中配置

注意:本教程重点介绍基于cookie的标准方法。 对于持久性化方法(即将token保存到数据库中),请查看 Spring Security –持久化“记住我”

2. Security 配置

基于Java的Security配置:

@Configuration
@EnableWebSecurity
public class SecSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean("authenticationManager")
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            .withUser("user1").password("{noop}user1Pass").roles("USER")
            .and()
            .withUser("admin1").password("{noop}admin1Pass").roles("ADMIN");
    }

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/anonymous*").anonymous()
            .antMatchers("/login*").permitAll()
            .anyRequest().authenticated()
            
            .and()
            .formLogin()
            .loginPage("/login.html")
            .loginProcessingUrl("/login")
            .failureUrl("/login.html?error=true")
            
            .and()
            .logout().deleteCookies("JSESSIONID")
            
            .and()
            .rememberMe().key("uniqueAndSecret")
            ;
    }
}

如你所见, 使用rememberMe()方法的基本配置非常简单 ,同时通过其他选项可以保持非常高的灵活性。这里key非常重要 – 它是整个应用程序的私有密钥,在生成token时将使用。

此外, 使用tokenValiditySeconds()可以配置token有效时间。比如由默认的二周,我们改为一天:

rememberMe().key("uniqueAndSecret").tokenValiditySeconds(86400)

我们还可以看一下等效的XML配置:

<http use-expressions="true">
    <intercept-url pattern="/anonymous*" access="isAnonymous()" />
    <intercept-url pattern="/login*" access="permitAll" />
    <intercept-url pattern="/**" access="isAuthenticated()" />

    <form-login login-page='/login.html' 
      authentication-failure-url="/login.html?error=true" />
    <logout delete-cookies="JSESSIONID" />

    <remember-me key="uniqueAndSecret"/>
</http>

<authentication-manager id="authenticationManager">
    <authentication-provider>
        <user-service>
            <user name="user1" password="{noop}user1Pass" authorities="ROLE_USER" />
            <user name="admin1" password="{noop}admin1Pass" authorities="ROLE_ADMIN" />
        </user-service>
    </authentication-provider>
</authentication-manager>

3. 登录表单

类似于前文Spring Security表单登录:

<html>
<head></head>

<body>
    <h1>Login</h1>

    <form name='f' action="login" method='POST'>
        <table>
            <tr>
                <td>User:</td>
                <td><input type='text' name='username' value=''></td>
            </tr>
            <tr>
                <td>Password:</td>
                <td><input type='password' name='password' /></td>
            </tr>
            <tr>
                <td>Remember Me:</td>
                <td><input type="checkbox" name="remember-me" /></td>
            </tr>
            <tr>
                <td><input name="submit" type="submit" value="submit" /></td>
            </tr>
        </table>
    </form>

</body>
</html>

只不过我们新增了一个”记住我“的复选框(checkbox )

默认字段名也可以被修改如下:

.rememberMe().rememberMeParameter("remember-me-new")

当用户登录时,该机制会创建一个额外的cookie –“remember-me” cookie。

Remember Me cookie 包含下面的数据:

  • username – 识别用户
  • expirationTime – cookie过期时间; 默认为2周
  • MD5 hash 对前2个值usernameexpirationTime以及password和预定义的key进行MD5得到的hash值

这里首先要注意的是,用户名和密码都是 Cookie 的一部分 - 这意味着,如果其中任何一个更改,Cookie 将不再有效。此外,用户名也可以从cookie中读取。

此外,需要重点提醒的是,cookie将一直有效并可用,直到过期或更改凭据为止。所以如果"remember-me" cookie被窃取,则该机制可能会受到攻击。

5. 实践

为了验证”记住我“,你可以:

  • 登录并勾选”记住我“
  • 等待session过期 (或者浏览器中删除JSESSIONID cookie)
  • 刷新页面

没有勾选"记住我",在cookie过期后,用户应该重定向回登录页面。 勾选后,用户能继续保持登录状态。

6. 总结

本教程介绍了如何在Security中配置“记住我”功能,并简要介绍了cookie中包含哪些类型的数据。

完整源代码可从Github上获取。

项目启动后, 访问登录页面http://localhost:8080/spring-security-mvc-custom/login.html。