1. 概述

在这个教程中,我们将继续正在进行的Spring Security注册系列,设置一个定时任务来清除过期的VerificationToken。在注册过程中,会持久化一个VerificationToken。本文将展示如何删除这些实体。

2. 删除过期令牌

回想一下系列前一篇文章中的内容,验证令牌有一个名为expiryDate的成员,代表令牌的过期时间戳:

@Entity
public class VerificationToken {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String token;

    @OneToOne(targetEntity = User.class, fetch = FetchType.EAGER)
    @JoinColumn(nullable = false, name = "user_id", 
      foreignKey = @ForeignKey(name="FK_VERIFY_USER"))
    private User user;

    private Date expiryDate;
    ...
}

我们将使用这个expiryDate属性生成Spring Data的查询。

如果你想要了解更多关于Spring Data JPA的信息,可以查看这篇文章

2.1. 删除操作

为了方便删除过期令牌,我们将在VerificationTokenRepository中添加一个新的方法:

public interface VerificationTokenRepository
  extends JpaRepository<VerificationToken, Long> {

    void deleteByExpiryDateLessThan(Date now);
}

使用查询关键字 LessThan表示Spring Data的查询创建机制只对我们感兴趣于expiryDate属性小于指定时间的令牌。

请注意,由于VerificationTokenUser之间有一个标记为FetchType.EAGER@OneToOne关联,即使deleteByExpiryDateLessThan方法的返回类型是void也会发出一个查询来填充User实体

select 
    *
from 
    VerificationToken verification 
where 
    verification.expiryDate < ?

select 
    * 
from
    user_account user 
where
    user.id=?

delete from 
    VerificationToken
where
    id=?

2.2. 使用JPQL删除

如果不需要将实体加载到持久化上下文中,我们可以创建一个JPQL查询:

public interface VerificationTokenRepository
  extends JpaRepository<VerificationToken, Long> {

    @Modifying
    @Query("delete from VerificationToken t where t.expiryDate <= ?1")
    void deleteAllExpiredSince(Date now);
}

然后Hibernate不会将实体加载到持久化上下文中:

delete from
    VerificationToken
where
    expiryDate <= ?

3. 定时任务:清除令牌

我们现在有了一个希望定期执行的查询。我们将使用Spring提供的调度支持,并创建一个方法来运行我们的删除逻辑。

如果你想要了解更多关于Spring作业调度框架的信息,可以查看这篇文章

3.1. 启用调度

要启用任务调度,我们创建一个新的配置类SpringTaskConfig,并使用@EnableScheduling注解:

@Configuration
@EnableScheduling
public class SpringTaskConfig {
    //
}

3.2. 清除令牌任务

在服务层中,我们使用当前时间调用我们的仓库。

然后,我们使用@Scheduled注解标记该方法,指示Spring应该定期执行它:

@Service
@Transactional
public class TokensPurgeTask {

    @Autowired
    private VerificationTokenRepository tokenRepository;

    @Scheduled(cron = "${purge.cron.expression}")
    public void purgeExpired() {
        Date now = Date.from(Instant.now());
        tokenRepository.deleteAllExpiredSince(now);
    }
}

3.3. 定时安排

我们使用一个属性来保存cron设置的值,以避免更改时重新编译。在application.properties中分配值:

#    5am every day
purge.cron.expression=0 0 5 * * ?

4. 总结

在这篇文章中,我们使用了Spring Data JPA解决了VerificationToken的删除问题。

我们展示了使用属性表达式创建查询来查找所有过期日期小于指定时间的令牌。并且我们创建了一个任务,在运行时调用这个清理逻辑,并将其注册到Spring作业调度框架中,以便定期执行。

此Spring Security注册教程的实现可以在GitHub项目中找到 - 这是一个基于Eclipse的项目,所以导入并直接运行应该很容易。