Spring Security REST:账号过期时如何失效Refresh Token?
解决Spring Security REST中账号过期后移动端Token未失效的问题
针对你遇到的账号过期(accountExpired=true)后移动端Token仍有效的问题,可通过以下几种方案实现权限终止:
方案1:自定义请求过滤器,实时校验用户状态
Spring Security REST默认仅在获取Token时校验用户状态,后续请求不再检查。我们可以添加一个过滤器,在每次API请求时重新校验用户的accountExpired状态,一旦发现账号过期立即拒绝访问。
实现步骤:
- 创建自定义过滤器类
class AccountStatusCheckFilter extends OncePerRequestFilter { def userService @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { Authentication auth = SecurityContextHolder.getContext().getAuthentication() if (auth?.principal instanceof User) { // 从数据库拉取最新的用户状态,避免缓存导致的状态不一致 User latestUser = userService.get(auth.principal.id) if (latestUser?.accountExpired) { SecurityContextHolder.clearContext() response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "账号已过期") return } } filterChain.doFilter(request, response) } }
- 注册过滤器Bean
在grails-app/conf/spring/resources.groovy中添加:
accountStatusCheckFilter(AccountStatusCheckFilter) { userService = ref('userService') }
- 将过滤器加入Spring Security链
修改grails-app/conf/application.groovy中的Spring Security配置:
grails.plugin.springsecurity.filterChain.chainMap = [ '/api/**': 'JOINED_FILTERS,-anonymousAuthenticationFilter,-exceptionTranslationFilter,-authenticationProcessingFilter,-securityContextPersistenceFilter', '/**': 'JOINED_FILTERS,-restTokenValidationFilter,-restExceptionTranslationFilter' ] grails.plugin.springsecurity.filterChain.filterNames = [ 'securityContextPersistenceFilter', 'restTokenValidationFilter', 'accountStatusCheckFilter', // 放在Token验证过滤器之后 'filterSecurityInterceptor' ]
方案2:维护Token黑名单,账号过期时禁用所有有效Token
如果使用的是持久化存储的Token(如数据库存储)或JWT,可通过黑名单机制直接禁用用户的所有有效Token。
实现步骤:
- 创建Token黑名单实体
class TokenBlacklist { String tokenValue Date expirationDate Date dateCreated static constraints = { tokenValue unique: true } }
- 编写黑名单服务
class TokenBlacklistService { boolean isBlacklisted(String token) { return TokenBlacklist.existsByTokenValue(token) } void addToBlacklist(String token, Date expirationDate) { new TokenBlacklist(tokenValue: token, expirationDate: expirationDate).save(flush: true) // 定期清理过期的黑名单记录 TokenBlacklist.where { expirationDate < new Date() }.deleteAll() } }
- 账号过期时批量加入黑名单
在设置用户accountExpired=true时,同步将该用户的所有有效Token加入黑名单:
def disableUser(User user) { user.accountExpired = true user.save(flush: true) // 假设你用了数据库存储的Refresh Token,替换为你的Token实体类 def activeTokens = RefreshToken.where { user == user && expiration > new Date() }.list() activeTokens.each { token -> tokenBlacklistService.addToBlacklist(token.token, token.expiration) } }
- 在Token验证环节校验黑名单
如果用JWT,自定义Token服务类:
class CustomJwtTokenService extends DefaultJwtTokenService { def tokenBlacklistService @Override protected Authentication parseToken(String token) { if (tokenBlacklistService.isBlacklisted(token)) { throw new InvalidTokenException("Token已被禁用") } super.parseToken(token) } }
方案3:缩短Access Token有效期,强制刷新时校验用户状态
将Access Token的有效期设短(如15分钟),用户需要频繁用Refresh Token刷新新的Access Token。而Spring Security REST在刷新Token时会重新校验用户状态,此时若accountExpired=true,会直接拒绝刷新请求,旧Token过期后用户无法继续访问。
配置示例(application.groovy):
grails.plugin.springsecurity.rest.token.storage.useJwt = true grails.plugin.springsecurity.rest.token.jwt.expiration = 900 // Access Token有效期15分钟 grails.plugin.springsecurity.rest.token.refresh.useJwt = true grails.plugin.springsecurity.rest.token.refresh.jwt.expiration = 86400 // Refresh Token有效期1天
方案选择建议
- 若需要立即终止权限:优先选择方案1+方案2,既实时拦截请求,又禁用已发出的Token。
- 若可接受短时间延迟:方案3配置最简单,仅需调整Token有效期,依赖Refresh Token刷新时的状态校验。
内容的提问来源于stack exchange,提问作者Victor Soares
相关产品推荐
相关产品推荐

