网站首页 网站源码
using System.Collections.Immutable;
using System.Text.Json;
using Dpz.Core.MongodbAccess;
using Dpz.Core.Public.Entity.Auth;
using Microsoft.Extensions.Logging;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
using OpenIddict.Abstractions;
namespace Dpz.Core.Auth.Service;
public sealed class DpzAuthorizationStore(
IRepository<DpzAuthorization> repository,
ILogger<DpzAuthorizationStore> logger
) : IOpenIddictAuthorizationStore<DpzAuthorization>
{
public async ValueTask<long> CountAsync(CancellationToken cancellationToken)
{
logger.LogInformation("Counting authorizations");
return await repository.MongodbQueryable.LongCountAsync(
cancellationToken: cancellationToken
);
}
public async ValueTask<long> CountAsync<TResult>(
Func<IQueryable<DpzAuthorization>, IQueryable<TResult>> query,
CancellationToken cancellationToken
)
{
logger.LogInformation("Counting authorizations with custom query");
return await query(repository.MongodbQueryable)
.LongCountAsync(cancellationToken: cancellationToken);
}
public async ValueTask CreateAsync(
DpzAuthorization dpzAuthorization,
CancellationToken cancellationToken
)
{
logger.LogInformation("Creating authorization {Id}", dpzAuthorization.Id);
await repository.InsertAsync(dpzAuthorization, cancellationToken);
}
public async ValueTask DeleteAsync(
DpzAuthorization dpzAuthorization,
CancellationToken cancellationToken
)
{
logger.LogInformation("Deleting authorization {Id}", dpzAuthorization.Id);
await repository.DeleteAsync(x => x.Id == dpzAuthorization.Id, cancellationToken);
}
public IAsyncEnumerable<DpzAuthorization> FindAsync(
string? subject,
string? client,
string? status,
string? type,
ImmutableArray<string>? scopes,
CancellationToken cancellationToken
)
{
logger.LogInformation(
"Find authorizations subject={Subject}, client={Client}, status={Status}, type={Type}",
subject,
client,
status,
type
);
var filter = Builders<DpzAuthorization>.Filter.Empty;
if (!string.IsNullOrWhiteSpace(subject))
{
filter &= Builders<DpzAuthorization>.Filter.Eq(x => x.Subject, subject);
}
if (!string.IsNullOrWhiteSpace(client))
{
if (ObjectId.TryParse(client, out var appId))
{
filter &= Builders<DpzAuthorization>.Filter.Eq(x => x.ApplicationId, appId);
}
else
{
// 如果传入的是客户端Id字符串,这里无法直接反查ApplicationId,按存储模型仅支持ObjectId
filter &= Builders<DpzAuthorization>.Filter.Where(_ => false);
}
}
if (!string.IsNullOrWhiteSpace(status))
{
filter &= Builders<DpzAuthorization>.Filter.Eq(x => x.Status, status);
}
if (!string.IsNullOrWhiteSpace(type))
{
filter &= Builders<DpzAuthorization>.Filter.Eq(x => x.Type, type);
}
if (scopes is { IsDefaultOrEmpty: false })
{
filter &= Builders<DpzAuthorization>.Filter.All(x => x.Scopes!, scopes.Value);
}
return repository.SearchForAsync(filter, cancellationToken);
}
public IAsyncEnumerable<DpzAuthorization> FindByApplicationIdAsync(
string identifier,
CancellationToken cancellationToken
)
{
logger.LogInformation("Find authorizations by application id {Identifier}", identifier);
if (!ObjectId.TryParse(identifier, out var appId))
{
return AsyncEnumerable.Empty<DpzAuthorization>();
}
var filter = Builders<DpzAuthorization>.Filter.Eq(x => x.ApplicationId, appId);
return repository.SearchForAsync(filter, cancellationToken);
}
public async ValueTask<DpzAuthorization?> FindByIdAsync(
string identifier,
CancellationToken cancellationToken
)
{
logger.LogInformation("Find authorization by id {Identifier}", identifier);
if (ObjectId.TryParse(identifier, out var id))
{
return await repository.FindAsync(id, cancellationToken);
}
return null;
}
public IAsyncEnumerable<DpzAuthorization> FindBySubjectAsync(
string subject,
CancellationToken cancellationToken
)
{
logger.LogInformation("Find authorizations by subject {Subject}", subject);
var filter = Builders<DpzAuthorization>.Filter.Eq(x => x.Subject, subject);
return repository.SearchForAsync(filter, cancellationToken);
}
public async ValueTask<string?> GetApplicationIdAsync(
DpzAuthorization dpzAuthorization,
CancellationToken cancellationToken
)
{
return await ValueTask.FromResult(dpzAuthorization.ApplicationId.ToString());
}
public async ValueTask<TResult?> GetAsync<TState, TResult>(
Func<IQueryable<DpzAuthorization>, TState, IQueryable<TResult>> query,
TState state,
CancellationToken cancellationToken
)
{
logger.LogInformation("Get with custom query for authorizations");
return await Task.FromResult(query(repository.MongodbQueryable, state).FirstOrDefault());
}
public async ValueTask<DateTimeOffset?> GetCreationDateAsync(
DpzAuthorization dpzAuthorization,
CancellationToken cancellationToken
)
{
var result = dpzAuthorization.CreationDate is { } dt
? new DateTimeOffset(DateTime.SpecifyKind(dt, DateTimeKind.Utc))
: (DateTimeOffset?)null;
return await ValueTask.FromResult(result);
}
public async ValueTask<string?> GetIdAsync(
DpzAuthorization dpzAuthorization,
CancellationToken cancellationToken
)
{
return await ValueTask.FromResult(dpzAuthorization.Id.ToString());
}
public async ValueTask<ImmutableDictionary<string, JsonElement>> GetPropertiesAsync(
DpzAuthorization dpzAuthorization,
CancellationToken cancellationToken
)
{
if (dpzAuthorization.Properties is null)
{
return await ValueTask.FromResult(ImmutableDictionary<string, JsonElement>.Empty);
}
// 将BsonDocument序列化为Json再解析为字典
var json = dpzAuthorization.Properties.ToJson();
var doc = JsonDocument.Parse(json);
var builder = ImmutableDictionary.CreateBuilder<string, JsonElement>();
foreach (var property in doc.RootElement.EnumerateObject())
{
builder[property.Name] = property.Value.Clone();
}
return await ValueTask.FromResult(builder.ToImmutable());
}
public async ValueTask<ImmutableArray<string>> GetScopesAsync(
DpzAuthorization dpzAuthorization,
CancellationToken cancellationToken
)
{
var list = dpzAuthorization.Scopes ?? [];
return await ValueTask.FromResult(ImmutableArray.CreateRange(list));
}
public async ValueTask<string?> GetStatusAsync(
DpzAuthorization dpzAuthorization,
CancellationToken cancellationToken
)
{
return await ValueTask.FromResult(dpzAuthorization.Status);
}
public async ValueTask<string?> GetSubjectAsync(
DpzAuthorization dpzAuthorization,
CancellationToken cancellationToken
)
{
return await ValueTask.FromResult(dpzAuthorization.Subject);
}
public async ValueTask<string?> GetTypeAsync(
DpzAuthorization dpzAuthorization,
CancellationToken cancellationToken
)
{
return await ValueTask.FromResult(dpzAuthorization.Type);
}
public async ValueTask<DpzAuthorization> InstantiateAsync(CancellationToken cancellationToken)
{
return await ValueTask.FromResult(new DpzAuthorization { Id = ObjectId.GenerateNewId() });
}
public IAsyncEnumerable<DpzAuthorization> ListAsync(
int? count,
int? offset,
CancellationToken cancellationToken
)
{
logger.LogInformation(
"List authorizations with pagination: offset={Offset}, count={Count}",
offset,
count
);
var options = new FindOptions<DpzAuthorization>();
if (offset.HasValue)
{
options.Skip = offset.Value;
}
if (count.HasValue)
{
options.Limit = count.Value;
}
return repository.SearchForAsync(
Builders<DpzAuthorization>.Filter.Empty,
options,
cancellationToken
);
}
public async IAsyncEnumerable<TResult> ListAsync<TState, TResult>(
Func<IQueryable<DpzAuthorization>, TState, IQueryable<TResult>> query,
TState state,
[System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken
)
{
logger.LogInformation("List authorizations with custom query state={State}", state);
var projected = query(repository.MongodbQueryable, state);
await foreach (
var element in projected.ToAsyncEnumerable().WithCancellation(cancellationToken)
)
{
yield return element;
}
}
public async ValueTask<long> PruneAsync(
DateTimeOffset threshold,
CancellationToken cancellationToken
)
{
logger.LogInformation("Prune authorizations before {Threshold}", threshold);
// 按创建时间早于阈值、且已无效状态,清理旧授权
var builder = Builders<DpzAuthorization>.Filter;
var filter = builder.And(
builder.Lt(x => x.CreationDate, threshold.UtcDateTime),
builder.Ne(x => x.Status, OpenIddictConstants.Statuses.Valid)
);
var result = await repository.Collection.DeleteManyAsync(filter, cancellationToken);
return result.DeletedCount;
}
public async ValueTask<long> RevokeAsync(
string? subject,
string? client,
string? status,
string? type,
CancellationToken cancellationToken
)
{
logger.LogInformation(
"Revoke authorizations subject={Subject}, client={Client}, status={Status}, type={Type}",
subject,
client,
status,
type
);
var builder = Builders<DpzAuthorization>.Filter;
var filter = builder.Empty;
if (!string.IsNullOrWhiteSpace(subject))
{
filter &= builder.Eq(x => x.Subject, subject);
}
if (!string.IsNullOrWhiteSpace(client) && ObjectId.TryParse(client, out var appId))
{
filter &= builder.Eq(x => x.ApplicationId, appId);
}
if (!string.IsNullOrWhiteSpace(status))
{
filter &= builder.Eq(x => x.Status, status);
}
if (!string.IsNullOrWhiteSpace(type))
{
filter &= builder.Eq(x => x.Type, type);
}
var update = Builders<DpzAuthorization>.Update.Set(
x => x.Status,
OpenIddictConstants.Statuses.Revoked
);
var result = await repository.Collection.UpdateManyAsync(
filter,
update,
cancellationToken: cancellationToken
);
return result.ModifiedCount;
}
public async ValueTask<long> RevokeByApplicationIdAsync(
string identifier,
CancellationToken cancellationToken
)
{
logger.LogInformation("Revoke authorizations by application id {Identifier}", identifier);
if (!ObjectId.TryParse(identifier, out var appId))
{
return 0L;
}
var filter = Builders<DpzAuthorization>.Filter.Eq(x => x.ApplicationId, appId);
var update = Builders<DpzAuthorization>.Update.Set(
x => x.Status,
OpenIddictConstants.Statuses.Revoked
);
var result = await repository.Collection.UpdateManyAsync(
filter,
update,
cancellationToken: cancellationToken
);
return result.ModifiedCount;
}
public async ValueTask<long> RevokeBySubjectAsync(
string subject,
CancellationToken cancellationToken
)
{
logger.LogInformation("Revoke authorizations by subject {Subject}", subject);
var filter = Builders<DpzAuthorization>.Filter.Eq(x => x.Subject, subject);
var update = Builders<DpzAuthorization>.Update.Set(
x => x.Status,
OpenIddictConstants.Statuses.Revoked
);
var result = await repository.Collection.UpdateManyAsync(
filter,
update,
cancellationToken: cancellationToken
);
return result.ModifiedCount;
}
public async ValueTask SetApplicationIdAsync(
DpzAuthorization dpzAuthorization,
string? identifier,
CancellationToken cancellationToken
)
{
if (ObjectId.TryParse(identifier, out var appId))
{
dpzAuthorization.ApplicationId = appId;
}
await ValueTask.CompletedTask;
}
public async ValueTask SetCreationDateAsync(
DpzAuthorization dpzAuthorization,
DateTimeOffset? date,
CancellationToken cancellationToken
)
{
dpzAuthorization.CreationDate = date?.UtcDateTime;
await ValueTask.CompletedTask;
}
public async ValueTask SetPropertiesAsync(
DpzAuthorization dpzAuthorization,
ImmutableDictionary<string, JsonElement> properties,
CancellationToken cancellationToken
)
{
if (properties.Count == 0)
{
dpzAuthorization.Properties = null;
await ValueTask.CompletedTask;
return;
}
var bson = new BsonDocument();
foreach (var kv in properties)
{
bson[kv.Key] = BsonDocument.Parse(kv.Value.GetRawText());
}
dpzAuthorization.Properties = bson;
await ValueTask.CompletedTask;
}
public async ValueTask SetScopesAsync(
DpzAuthorization dpzAuthorization,
ImmutableArray<string> scopes,
CancellationToken cancellationToken
)
{
dpzAuthorization.Scopes = scopes.IsDefault ? Array.Empty<string>() : scopes.ToArray();
await ValueTask.CompletedTask;
}
public async ValueTask SetStatusAsync(
DpzAuthorization dpzAuthorization,
string? status,
CancellationToken cancellationToken
)
{
dpzAuthorization.Status = status;
await ValueTask.CompletedTask;
}
public async ValueTask SetSubjectAsync(
DpzAuthorization dpzAuthorization,
string? subject,
CancellationToken cancellationToken
)
{
dpzAuthorization.Subject = subject;
await ValueTask.CompletedTask;
}
public async ValueTask SetTypeAsync(
DpzAuthorization dpzAuthorization,
string? type,
CancellationToken cancellationToken
)
{
dpzAuthorization.Type = type;
await ValueTask.CompletedTask;
}
public async ValueTask UpdateAsync(
DpzAuthorization dpzAuthorization,
CancellationToken cancellationToken
)
{
logger.LogInformation("Update authorization {Id}", dpzAuthorization.Id);
await repository.UpdateAsync(dpzAuthorization, cancellationToken);
}
}
上述代码定义了一个名为 DpzAuthorizationStore 的类,它实现了 IOpenIddictAuthorizationStore<DpzAuthorization> 接口。这个类主要用于管理与授权相关的操作,特别是在使用 MongoDB 作为数据存储的情况下。以下是代码的主要功能和结构的详细解释:
计数操作:
CountAsync: 计算存储中的授权数量。CountAsync<TResult>: 根据自定义查询计算授权数量。创建和删除授权:
CreateAsync: 创建新的授权记录。DeleteAsync: 根据授权对象的 ID 删除授权记录。查找授权:
FindAsync: 根据多个条件(如 subject、client、status、type 和 scopes)查找授权记录。FindByApplicationIdAsync: 根据应用程序 ID 查找授权记录。FindByIdAsync: 根据授权 ID 查找单个授权记录。FindBySubjectAsync: 根据 subject 查找授权记录。获取授权属性:
GetApplicationIdAsync: 获取授权的应用程序 ID。GetCreationDateAsync: 获取授权的创建日期。GetIdAsync: 获取授权的 ID。GetPropertiesAsync: 获取授权的附加属性。GetScopesAsync: 获取授权的作用域。GetStatusAsync: 获取授权的状态。GetSubjectAsync: 获取授权的 subject。GetTypeAsync: 获取授权的类型。实例化和列出授权:
InstantiateAsync: 创建一个新的 DpzAuthorization 实例。ListAsync: 列出所有授权记录,支持分页。ListAsync<TState, TResult>: 根据自定义查询列出授权记录。撤销和修剪授权:
RevokeAsync: 撤销符合条件的授权记录。RevokeByApplicationIdAsync: 根据应用程序 ID 撤销授权。RevokeBySubjectAsync: 根据 subject 撤销授权。PruneAsync: 删除创建日期早于指定阈值且状态无效的授权记录。设置授权属性:
SetApplicationIdAsync: 设置授权的应用程序 ID。SetCreationDateAsync: 设置授权的创建日期。SetPropertiesAsync: 设置授权的附加属性。SetScopesAsync: 设置授权的作用域。SetStatusAsync: 设置授权的状态。SetSubjectAsync: 设置授权的 subject。SetTypeAsync: 设置授权的类型。更新授权:
UpdateAsync: 更新现有的授权记录。IRepository<DpzAuthorization> 和一个 ILogger<DpzAuthorizationStore>,用于数据访问和日志记录。ValueTask 和 IAsyncEnumerable,以支持异步编程模型,优化性能。MongoDB.Driver 提供的构建器来构建查询和更新操作,确保与 MongoDB 的交互高效且安全。DpzAuthorizationStore 类是一个用于管理授权的存储库,提供了创建、查找、更新、删除和撤销授权的功能。它利用 MongoDB 作为后端存储,并通过日志记录提供了操作的可追踪性。这个类在实现 OAuth2 或 OpenID Connect 等身份验证和授权协议时非常有用。
