前言
SpringSecurity自带了RemeberMe的功能,后端只需简单配置即可实现,网上的教程千篇一律,这篇只简单介绍下背景,并说说我遇到的问题和解决方法
配置
remember-me默认配置
其实就是加一句代码,rememberMe()方法,则自动开启,即可实现功能,但是使用默认的存在一些问题:
- 1.默认的的rememberMe功能,前端无法自定义名称必须传入remember-me值(一般是input checkbox,传入后台值是on),过滤器才生效,返回的cookie也是固定名称remember-me-cookie
- 2.默认自动登录时间为14天,无法灵活配置
- 3.默认使用的InMemoryTokenRepositoryImpl的存储方式(即内存),当服务宕机或者重启后,session失效,那么前端传入的token无法校验导致自动登录失效,体验上有问题
自定义jdbc配置
为了解决上面问题,需要一些特殊配置,首先是需要自定义传值名称和cookie名称,以及设置自定义时间
加入这三句代码即可配置,可将属性值改成自定义,我配置的2天过期失效。
修改持久化方法,改为数据库存储,SpringSecurity提供了默认的sql存储,包括增删改,需要导入默认的table:persistent_logins,有方法自动建表无需手动导入
@Autowired
private DataSource dataSource;
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl persistentTokenRepository = new JdbcTokenRepositoryImpl();
persistentTokenRepository.setDataSource(dataSource);
//自动建表,建完注释掉
// persistentTokenRepository.setCreateTableOnStartup(true);
return persistentTokenRepository;
}
dataSource为你在properties或者yaml中自定义的数据源,可以断点看下是否引入成功,或者直接看自动建表是否成功,成功说明dataSource引入没问题
遇到的问题
存储失败
当测试remember-me的功能时,发现并没有设置成功,persistent_logins表中也没有存对应的token,通过debug源码发现,登录成功后本应该走到PersistentTokenBasedRememberMeServices的onLoginSuccess的方法上,结果并没有进入,Google后在stack overflow提到同样问题。
经过源码排查发现,这个方法在入口在PersistentTokenBasedRememberMeServices.java中
而方法的调用在springsecurity内置的filter中,如果想只通过配置就能实现remember-me功能,你的filter需要实现UsernamePasswordAuthenticationFilter或者使用httpBasic()走BasicAuthenticationFilter 自带的过滤器才行!
由于我已经自定义了过滤器不想再去重复开发,决定手动引入这个方法,具体操作如下:
- 1.注入PersistentTokenRepository,配置dataSource连接池,上面代码已写
- 2.注入PersistentTokenBasedRememberMeServices和UserDetailsService
@Bean
public PersistentTokenBasedRememberMeServices getPersistentTokenBasedRememberMeServices() {
PersistentTokenBasedRememberMeServices persistenceTokenBasedService = new PersistentTokenBasedRememberMeServices(
"rememberMe", userDetailsService(), persistentTokenRepository());
persistenceTokenBasedService.setAlwaysRemember(true);
return persistenceTokenBasedService;
}
@Override
protected UserDetailsService userDetailsService() {
return userDetailsService;
}
- 3.这样你就可以在你登录的方法中调用loginSuccess方法了
private final PersistentTokenBasedRememberMeServices rememberMeServices;
@ApiOperation("登录授权")
@RequestMapping(value = "/login", method = RequestMethod.POST)
public Result<Object> login(@Validated @RequestBody AuthUserDTO authUser, HttpServletRequest request, HttpServletResponse response) {
// ```登录认证略
Authentication auth =SecurityContextHolder.getContext().getAuthentication(); if (auth != null && StringUtils.isNotEmpty(authUser.getRememberMe())) {
//调用
persistentTokenBasedRememberMeServices.loginSuccess(request, response, auth);
}
return Result.success(authInfo);
}
- 4.用户退出登录,记得清理session
@ApiOperation("退出登录")
@RequestMapping(value = "/logout", method = RequestMethod.DELETE)
@Log("退出登录")
public Result<Object> logout(HttpServletRequest request, HttpServletResponse response) {
//···退出逻辑略
//清除session
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null) {
persistentTokenBasedRememberMeServices.logout(request, response, auth);
}
return Result.success();
}
使用apifox测试登录接口,发现数据库已有相关token信息,成功解决。
Ps.为了矿石我也是拼辣!