下面是关于“Spring Security 实现‘记住我’功能及原理解析”的完整攻略。
1. Spring Security “记住我”功能原理
1.1 什么是“记住我”功能
“记住我”是指,在浏览器关闭后,再次打开浏览器后用户仍然不需要重新登录,直接就可以访问受保护的资源。这个功能在某些情况下非常方便,比如在家里用个人电脑访问自己的博客网站,不想每次都登录。
1.2 “记住我”功能的原理
“记住我”功能的实现原理是在用户第一次登录成功后,在服务器端生成一个 “remember-me" cookie。这个cookie中包含用户名、密码和一个过期时间。当用户再次访问该网站时,服务器可以根据“remember-me”cookie的信息来自动登录用户,而无需再次输入用户名和密码。
2. Spring Security “记住我”功能实现
2.1 配置记住我功能
要使用Spring Security提供的“记住我”功能,需要在Security配置类中进行如下配置:
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// http请求的安全配置
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.rememberMe() // 开启remember me 功能
.tokenValiditySeconds(60*60*24*7) // token 的有效时间为一周
.rememberMeParameter("remember-me") // 设置前端的remember me 参数名
.key("uniqueAndSecret") // 存储cookie的key
.and()
.logout()
.permitAll();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// 用户名密码配置,这里使用内存来配置用户
auth
.inMemoryAuthentication()
.withUser("user").password("{noop}password").roles("USER")
.and()
.withUser("admin").password("{noop}password").roles("USER", "ADMIN");
}
}
2.2 在登录表单中添加“记住我”选项
你需要在登录表单中添加“记住我”选项,例如:
<form class="form-signin" action="/login" method="post">
<h2 class="form-signin-heading">请登录</h2>
<label for="inputEmail" class="sr-only">用户名</label>
<input type="text" id="inputEmail" name="username" class="form-control" placeholder="用户名" required autofocus>
<label for="inputPassword" class="sr-only">密码</label>
<input type="password" id="inputPassword" name="password" class="form-control" placeholder="密码" required>
<div class="checkbox mb-3">
<label>
<input type="checkbox" name="remember-me" value="true"> 记住我
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">登录</button>
</form>
2.3 添加“记住我”测试示例
当用户输入用户名和密码并勾选“记住我”选项后,Spring Security会在服务器端生成一个“remember-me”cookie并将其发送给客户端的浏览器。当浏览器再次访问该网站时,将自动以“remember-me”cookie中的信息来自动登录用户。
下面是一个简单的测试示例:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class RememberMeTests {
@Autowired
private TestRestTemplate restTemplate;
@Test
public void testWithRememberMe() {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
// 1. 发送登录表单请求并携带用户名密码和“记住我”选项
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("username", "admin");
map.add("password", "password");
map.add("remember-me", "true");
HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(map, headers);
ResponseEntity<String> response = restTemplate.postForEntity("/login", entity, String.class);
// 此处断言登录成功并返回首页
assertEquals(HttpStatus.OK, response.getStatusCode());
assertTrue(response.getBody().contains("首页"));
// 2. 第一次使用 remember-me Cookie 访问受保护的页面 '/admin',此处也应该返回首页
headers.set("Cookie", response.getHeaders().getFirst("Set-Cookie"));
ResponseEntity<String> response2 = restTemplate.exchange("/admin", HttpMethod.GET, new HttpEntity<>(headers), String.class);
assertEquals(HttpStatus.OK, response2.getStatusCode());
assertTrue(response2.getBody().contains("首页"));
// 3. 第二次使用 remember-me Cookie 访问受保护的页面 '/admin',此处应该访问 '/admin' 页面
ResponseEntity<String> response3 = restTemplate.exchange("/admin", HttpMethod.GET, new HttpEntity<>(headers), String.class);
assertEquals(HttpStatus.OK, response3.getStatusCode());
assertTrue(response3.getBody().contains("管理员页面"));
}
}
2.4 添加“记住我”自动登录失败的测试示例
如果remember-me cookie已过期或者用户名和密码已经变更,则remember me功能验证将不会通过,为了验证这一点,下面是一个自动登录失败的测试示例:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class RememberMeFailureTests {
@Autowired
private TestRestTemplate restTemplate;
@Test
public void testWithRememberMe() {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.TEXT_HTML));
// 1. 发送带 remember-me 选项的登录表单,登录成功且生成 cookie
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("username", "admin");
map.add("password", "password");
map.add("remember-me", "true");
HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(map, headers);
ResponseEntity<String> response = restTemplate.postForEntity("/login", entity, String.class);
// 此处断言登录成功并返回首页
assertEquals(HttpStatus.OK, response.getStatusCode());
assertTrue(response.getBody().contains("首页"));
// 2. 删除 '/admin' 中登录后,由 remember me 产生的 session 属性
headers.set("Cookie", response.getHeaders().getFirst("Set-Cookie"));
restTemplate.execute("/admin", HttpMethod.DELETE, null, null);
// 3. 第一次使用 remember-me Cookie 访问 '/admin',此处应该访问登录页
ResponseEntity<String> response2 = restTemplate.exchange("/admin", HttpMethod.GET, new HttpEntity<>(headers), String.class);
assertEquals(HttpStatus.OK, response2.getStatusCode());
assertTrue(response2.getBody().contains("请登录"));
// 4. 第二次使用 remember-me Cookie 访问 '/admin',此处也应该访问登录页
ResponseEntity<String> response3 = restTemplate.exchange("/admin", HttpMethod.GET, new HttpEntity<>(headers), String.class);
assertEquals(HttpStatus.OK, response3.getStatusCode());
assertTrue(response3.getBody().contains("请登录"));
}
}
以上就是关于Spring Security实现“记住我”功能及原理解析的完整攻略,具体请参考代码示例。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring Security 实现“记住我”功能及原理解析 - Python技术站