You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Azure函数中清理OpenIddict过期无效的令牌与授权?

OpenIddict过期令牌/授权清理的Azure函数实现方案

一、核心清理逻辑

  • 针对OpenIddictTokens表:清理过期令牌(ExpirationDate小于当前UTC时间)、无效令牌(Status为Revoked或Invalidated)
  • 针对OpenIddictAuthorizations表:清理自身状态为Revoked/Invalidated的授权,以及关联的令牌已全部失效的授权

二、Azure函数定时触发配置

用Timer触发器设置执行频率,比如每天凌晨2点执行(Cron表达式可按需调整):

[FunctionName("OpenIddictCleanupFunction")]
public async Task Run([TimerTrigger("0 0 2 * * *")] TimerInfo myTimer, ILogger log, IServiceProvider serviceProvider)
{
    // 清理逻辑调用
}

注:Cron表达式0 0 2 * * *表示UTC时间每天2点执行,若要适配本地时区,可在Azure函数配置中设置时区。

三、基于OpenIddict API的具体实现(EF Core场景)

通过OpenIddict内置的存储API操作,避免直接写裸SQL,兼容性更好:

1. 清理服务实现

using OpenIddict.Core;
using OpenIddict.EntityFrameworkCore.Models;
using Microsoft.EntityFrameworkCore;

public class OpenIddictCleanupService
{
    private readonly IOpenIddictTokenManager<OpenIddictEntityFrameworkCoreToken> _tokenManager;
    private readonly IOpenIddictAuthorizationManager<OpenIddictEntityFrameworkCoreAuthorization> _authorizationManager;
    private readonly ILogger<OpenIddictCleanupService> _logger;
    private const int _batchSize = 1000;

    public OpenIddictCleanupService(
        IOpenIddictTokenManager<OpenIddictEntityFrameworkCoreToken> tokenManager,
        IOpenIddictAuthorizationManager<OpenIddictEntityFrameworkCoreAuthorization> authorizationManager,
        ILogger<OpenIddictCleanupService> logger)
    {
        _tokenManager = tokenManager;
        _authorizationManager = authorizationManager;
        _logger = logger;
    }

    public async Task CleanupExpiredAndInvalidEntitiesAsync()
    {
        var now = DateTimeOffset.UtcNow;
        await CleanupTokensAsync(now);
        await CleanupAuthorizationsAsync(now);
    }

    private async Task CleanupTokensAsync(DateTimeOffset now)
    {
        int page = 0;
        while (true)
        {
            // 分段加载待删除令牌,避免内存溢出
            var tokens = await _tokenManager.ListAsync(
                token => token.ExpirationDate <= now || 
                         token.Status is OpenIddictConstants.Statuses.Revoked or OpenIddictConstants.Statuses.Invalidated,
                query => query.Skip(page * _batchSize).Take(_batchSize)
            ).ToListAsync();

            if (!tokens.Any()) break;

            foreach (var token in tokens)
            {
                try
                {
                    await _tokenManager.DeleteAsync(token);
                    _logger.LogInformation("已删除无效令牌: {TokenId}", token.Id);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "删除令牌 {TokenId} 失败", token.Id);
                }
            }
            page++;
        }
    }

    private async Task CleanupAuthorizationsAsync(DateTimeOffset now)
    {
        int page = 0;
        while (true)
        {
            // 筛选自身无效或无有效令牌的授权
            var authorizations = await _authorizationManager.ListAsync(
                auth => auth.Status is OpenIddictConstants.Statuses.Revoked or OpenIddictConstants.Statuses.Invalidated ||
                        !auth.Tokens.Any(token => token.ExpirationDate > now && token.Status == OpenIddictConstants.Statuses.Valid),
                query => query.Skip(page * _batchSize).Take(_batchSize)
            ).ToListAsync();

            if (!authorizations.Any()) break;

            foreach (var auth in authorizations)
            {
                try
                {
                    await _authorizationManager.DeleteAsync(auth);
                    _logger.LogInformation("已删除无效授权: {AuthId}", auth.Id);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "删除授权 {AuthId} 失败", auth.Id);
                }
            }
            page++;
        }
    }
}

2. Azure函数中调用清理服务

[FunctionName("OpenIddictCleanupFunction")]
public async Task Run([TimerTrigger("0 0 2 * * *")] TimerInfo myTimer, ILogger log, IServiceProvider serviceProvider)
{
    using var scope = serviceProvider.CreateScope();
    var cleanupService = scope.ServiceProvider.GetRequiredService<OpenIddictCleanupService>();
    
    log.LogInformation("开始执行OpenIddict清理任务");
    await cleanupService.CleanupExpiredAndInvalidEntitiesAsync();
    log.LogInformation("OpenIddict清理任务执行完成");
}

四、Azure环境适配优化点

  • 数据库索引优化:在OpenIddictTokens表的ExpirationDate、Status字段,OpenIddictAuthorizations表的Status字段创建索引,避免全表扫描拖慢查询速度。
  • 连接配置:Azure函数的数据库连接字符串统一用应用服务配置(Application Settings)管理,同时调整连接超时时间(建议设为300秒),适配大数据量删除操作。
  • 监控告警:通过Azure Monitor收集清理日志,配置失败告警规则,及时发现异常。
  • 事务控制:大数据量清理时建议不要用全局事务,分批次独立删除,减少数据库锁表风险。

内容的提问来源于stack exchange,提问作者Ali Khan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 15:35:20