前段时间决定使用springsecurity来做用户权限,springsecurity功能非常强大,而且配置灵活,足以满足一般的权限要求。其中有一个功能是remember-me,也就是自动登录,在参考网上的例子后也能正常实现基本要求,但是我们有个需求是需要变更用户名,在正常修改了用户信息后,session过期了再刷新页面便要重新登录,失去了自动登录功能。
经过分析后确定,就是因为用户名改了,所以cookie对应的数据库持久化token就无法使用了(在这里我们使用的是PersistentTokenRepository,可以持久化token),所以现在要解决的就是在用户名修改后也要更新页面的cookie,而且需要保存新的token,下面是代码实现。
@RestController
public class UserController {
@Autowired
private UserService userService;
@Autowired
@Lazy
private RememberMeServices rememberMeService;
@PostMapping("/bindPhone")
public ReturnMsg bindPhone(@NotNull(message = "验证码不能为空") @Pattern(regexp = "^1234$", message = "验证码不正确") String code,
@NotNull(message = "手机号不能为空") @Pattern(regexp = "^\\d{11}$", message = "手机号码不正确") String phone) {
checkPhone(phone);
UserEntity user = getUser();
user.setPhone(phone);
userService.updateUser(user);
refreshAuthentication(request, response, generateNewAuthentication(user));
return ReturnMsg.successTip();
}
// 刷新authentication和cookie
private void refreshAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
if(checkCookie(request)){
rememberMeService.loginSuccess(request, response, authentication);
}
SecurityContextHolder.getContext().setAuthentication(authentication);
}
// 判断是否有remember-me的cookie,有才执行remember-me的后续
private boolean checkCookie(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
if(cookies != null){
for (Cookie cookie : cookies) {
if("remember-me".equals(cookie.getName()))
return true;
}
}
return false;
}
}
- 注入RememberMeServices(通过debug发现注入的其实就是PersistentTokenBasedRememberMeServices),RememberMeServices是springsecurity管理的,这里我们借用一下,特别注意这里一定要加上@Lazy注解,因为这个实例是security在构建的时候才创建的,不加会报错
@Bean
//@Lazy
public RememberMeServices rememberMeServices() throws Exception {
return getHttp().getSharedObject(RememberMeServices.class);
}
- 在controller中正常修改用户信息后,执行
rememberMeService.loginSuccess(request, response, authentication)
也就是创建新的cookie和持久化新的用户token,当session过期后也能根据新的cookie找到对应的用户信息实现自动登录
到这里其实已经能实现修改用户名后的后续自动登录了,但是你去看一下 persistent_logins(持久化token的表)会发现旧的token依旧存在表里。这是因为上面我们执行rememberMeService#loginSuccess这个方法不会删除旧的token,所以再完善一点的就是要写一个方法根据旧的用户名删除这个旧的token数据。
其实我也试过把注入的RememberMeServices强转为PersistentTokenBasedRememberMeServices,但是发现PersistentTokenBasedRememberMeServices不是注入的RememberMeServices的实例,debug发现是一个jdkProxy的代理类,后面就不去尝试了