簡體   English   中英

Identity Server 4-將刷新令牌保存在數據庫中

[英]Identity Server 4 - saving refresh token in datatabase

我正在.Net Core 2.0中使用IdentityServer4,並且已成功生成訪問令牌和刷新令牌。 我只需要能夠在服務器端“刷新”令牌生成時就可以看到它,以便出於某些特定目的將其保存在數據庫中。

在服務器上生成刷新令牌時,如何訪問它的值?

根據評論,我認為這對您和其他您的案件將是一個有用的解決方案。

我從圍繞IdentityServer本身的事情開始。 由於強烈建議在生產環境中使用您自己的PersistedGrant存儲,因此我們需要覆蓋默認存儲。

首先-在Startup.cs中:

services.AddTransient<IPersistedGrantStore, PersistedGrantStore>();

這將使用我們自己的PersistedGrantStore類實現其IPersistedGrantStore接口。

類本身:

public class PersistedGrantStore : IPersistedGrantStore
{
    private readonly ILogger logger;
    private readonly IPersistedGrantService persistedGrantService;

    public PersistedGrantStore(IPersistedGrantService persistedGrantService, ILogger<PersistedGrantStore> logger)
    {
        this.logger = logger;
        this.persistedGrantService = persistedGrantService;
    }

    public Task StoreAsync(PersistedGrant token)
    {
        var existing = this.persistedGrantService.Get(token.Key);
        try
        {
            if (existing == null)
            {
                logger.LogDebug("{persistedGrantKey} not found in database", token.Key);

                var persistedGrant = token.ToEntity();
                this.persistedGrantService.Add(persistedGrant);
            }
            else
            {
                logger.LogDebug("{persistedGrantKey} found in database", token.Key);

                token.UpdateEntity(existing);
                this.persistedGrantService.Update(existing);
            }
        }
        catch (DbUpdateConcurrencyException ex)
        {
            logger.LogWarning("exception updating {persistedGrantKey} persisted grant in database: {error}", token.Key, ex.Message);
        }

        return Task.FromResult(0);
    }

    public Task<PersistedGrant> GetAsync(string key)
    {
        var persistedGrant = this.persistedGrantService.Get(key);
        var model = persistedGrant?.ToModel();

        logger.LogDebug("{persistedGrantKey} found in database: {persistedGrantKeyFound}", key, model != null);

        return Task.FromResult(model);
    }

    public Task<IEnumerable<PersistedGrant>> GetAllAsync(string subjectId)
    {
        var persistedGrants = this.persistedGrantService.GetAll(subjectId).ToList();

        var model = persistedGrants.Select(x => x.ToModel());

        logger.LogDebug("{persistedGrantCount} persisted grants found for {subjectId}", persistedGrants.Count, subjectId);

        return Task.FromResult(model);
    }

    public Task RemoveAsync(string key)
    {
        var persistedGrant = this.persistedGrantService.Get(key);

        if (persistedGrant != null)
        {
            logger.LogDebug("removing {persistedGrantKey} persisted grant from database", key);

            try
            {
                this.persistedGrantService.Remove(persistedGrant);
            }
            catch (DbUpdateConcurrencyException ex)
            {
                logger.LogInformation("exception removing {persistedGrantKey} persisted grant from database: {error}", key, ex.Message);
            }
        }
        else
        {
            logger.LogDebug("no {persistedGrantKey} persisted grant found in database", key);
        }

        return Task.FromResult(0);
    }

    public Task RemoveAllAsync(string subjectId, string clientId)
    {
        var persistedGrants = this.persistedGrantService.GetAll(subjectId, clientId);

        logger.LogDebug("removing {persistedGrantCount} persisted grants from database for subject {subjectId}, clientId {clientId}", persistedGrants.Count(), subjectId, clientId);

        try
        {
            this.persistedGrantService.RemoveAll(persistedGrants);
        }
        catch (DbUpdateConcurrencyException ex)
        {
            logger.LogInformation("removing {persistedGrantCount} persisted grants from database for subject {subjectId}, clientId {clientId}: {error}", persistedGrants.Count(), subjectId, clientId, ex.Message);
        }

        return Task.FromResult(0);
    }

    public Task RemoveAllAsync(string subjectId, string clientId, string type)
    {
        var persistedGrants = this.persistedGrantService.GetAll(subjectId, clientId, type);

        logger.LogDebug("removing {persistedGrantCount} persisted grants from database for subject {subjectId}, clientId {clientId}, grantType {persistedGrantType}", persistedGrants.Count(), subjectId, clientId, type);

        try
        {
            this.persistedGrantService.RemoveAll(persistedGrants);
        }
        catch (DbUpdateConcurrencyException ex)
        {
            logger.LogInformation("exception removing {persistedGrantCount} persisted grants from database for subject {subjectId}, clientId {clientId}, grantType {persistedGrantType}: {error}", persistedGrants.Count(), subjectId, clientId, type, ex.Message);
        }

        return Task.FromResult(0);
    }
}

如您所見,我有一個界面和記錄器。

IPersistedGrantService接口:

public interface IPersistedGrantService
{
    void Add(PersistedGrantInfo persistedGrant);

    void Update(PersistedGrantInfo existing);

    PersistedGrantInfo Get(string key);

    IEnumerable<PersistedGrantInfo> GetAll(string subjectId);

    IEnumerable<PersistedGrantInfo> GetAll(string subjectId, string clientId);

    IEnumerable<PersistedGrantInfo> GetAll(string subjectId, string clientId, string type);

    void Remove(PersistedGrantInfo persistedGrant);

    void RemoveAll(IEnumerable<PersistedGrantInfo> persistedGrants);
}

如您所見,存在一個名為PersistedGrantInfo的對象。 這是我的DTO,用於在db實體和IDS4實體之間進行映射(您不必強制使用它,但我正在這樣做是為了獲得更好的抽象性)。

此信息對象通過AutoMapper映射到IDS4實體:

public static class PersistedGrantMappers
{
    internal static IMapper Mapper { get; }

    static PersistedGrantMappers()
    {
        Mapper = new MapperConfiguration(cfg => cfg.AddProfile<PersistedGrantMapperProfile>())
            .CreateMapper();
    }

    /// <summary>
    /// Maps an entity to a model.
    /// </summary>
    /// <param name="entity">The entity.</param>
    /// <returns></returns>
    public static PersistedGrant ToModel(this PersistedGrantInfo entity)
    {
        return entity == null ? null : Mapper.Map<PersistedGrant>(entity);
    }

    /// <summary>
    /// Maps a model to an entity.
    /// </summary>
    /// <param name="model">The model.</param>
    /// <returns></returns>
    public static PersistedGrantInfo ToEntity(this PersistedGrant model)
    {
        return model == null ? null : Mapper.Map<PersistedGrantInfo>(model);
    }

    /// <summary>
    /// Updates an entity from a model.
    /// </summary>
    /// <param name="model">The model.</param>
    /// <param name="entity">The entity.</param>
    public static void UpdateEntity(this PersistedGrant model, PersistedGrantInfo entity)
    {
        Mapper.Map(model, entity);
    }
}

以及映射器配置文件:

public class PersistedGrantMapperProfile:Profile
{
    /// <summary>
    /// <see cref="PersistedGrantMapperProfile">
    /// </see>
    /// </summary>
    public PersistedGrantMapperProfile()
    {
        CreateMap<PersistedGrantInfo, IdentityServer4.Models.PersistedGrant>(MemberList.Destination)
            .ReverseMap();
    }
}

回到IPersistedGrantService實現由您決定。 當前作為數據庫實體,我具有IDS4實體的精確副本:

 public class PersistedGrant
{
    [Key]
    public string Key { get; set; }

    public string Type { get; set; }

    public string SubjectId { get; set; }

    public string ClientId { get; set; }

    public DateTime CreationTime { get; set; }

    public DateTime? Expiration { get; set; }

    public string Data { get; set; }
}

但是根據您的需要,您可以做一些不同的事情(將此數據存儲在不同的表中,使用不同的列名等)。 然后在我的服務實現中,我只是使用來自IPersistedGrantStore實現中的數據,並對數據庫上下文中的實體進行CRUD處理。

得出結論-這里的主要任務是根據您的需要覆蓋/實現其IPersistedGrantStore接口。 希望這會有所幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM