gehgassi_backend/gehGassi.Web/Auth/DistributedCacheTicketStore.cs

205 lines
7.1 KiB
C#

using System;
using System.Threading.Tasks;
using gehGassi.Core.Interfaces;
using gehGassi.Web.Helper;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace gehGassi.Web.Auth
{
/// <summary>
/// Verteilter Ticketstore für AuthenticationTickets
/// </summary>
public class DistributedCacheTicketStore : ITicketStore
{
private readonly IOptions<SessionSettings> _sessionSettings;
private const string KeyPrefix = "AuthSessionStore-";
private readonly IDistributedCache _cache;
public DistributedCacheTicketStore(IOptions<SessionSettings> sessionSettings, IDistributedCache cache)
{
_sessionSettings = sessionSettings;
_cache = cache;
}
public async Task<string> StoreAsync(AuthenticationTicket ticket)
{
var key = KeyPrefix + ticket.Principal.Identity.Name;
await RenewAsync(key, ticket);
return key;
}
public Task RenewAsync(string key, AuthenticationTicket ticket)
{
if (!key.StartsWith(KeyPrefix))
key = KeyPrefix + key;
var options = new DistributedCacheEntryOptions();
var expiresUtc = ticket.Properties.ExpiresUtc;
if (expiresUtc.HasValue)
{
options.SetAbsoluteExpiration(expiresUtc.Value);
}
options.SetSlidingExpiration(TimeSpan.FromMinutes(_sessionSettings.Value.TimeOut));
var serializedTicket = TicketSerializer.Default.Serialize(ticket);
_cache.Set(key, serializedTicket, options);
return Task.FromResult(0);
}
public Task<AuthenticationTicket> RetrieveAsync(string key)
{
if (!key.StartsWith(KeyPrefix))
key = KeyPrefix + key;
AuthenticationTicket ticket = null;
var storedTicket = _cache.Get(key);
if (storedTicket != null)
{
ticket = TicketSerializer.Default.Deserialize(storedTicket);
}
return Task.FromResult(ticket);
}
public Task RemoveAsync(string key)
{
if (!key.StartsWith(KeyPrefix))
key = KeyPrefix + key;
_cache.Remove(key);
return Task.FromResult(0);
}
}
/// <summary>
/// Verteilter Ticketstore für AuthenticationTickets der diese initial von einer DB lädt und wieder speichern kann.
/// Übersteht "Server-Resets"
/// </summary>
public class DistributedCachePersistedTicketStore : ITicketStore
{
private bool _initialized;
private IServiceProvider _serivceProvider;
private readonly IOptions<SessionSettings> _sessionSettings;
private const string KeyPrefix = "AuthSessionStore-";
private readonly IDistributedCache _cache;
public DistributedCachePersistedTicketStore(IOptions<SessionSettings> sessionSettings, IDistributedCache cache, IServiceProvider serviceProvider)
{
_sessionSettings = sessionSettings;
_cache = cache;
_initialized = false;
_serivceProvider = serviceProvider;
}
private async Task InitializeAsync()
{
using var scope = _serivceProvider.CreateScope();
var persistedTicketService = scope.ServiceProvider.GetRequiredService<IPersistedTicketService>();
var items = await persistedTicketService.GetAllAsync();
foreach (var item in items)
{
var options = new DistributedCacheEntryOptions();
var expiresUtc = item.Expires;
if (expiresUtc.HasValue)
{
options.SetAbsoluteExpiration(expiresUtc.Value);
}
options.SetSlidingExpiration(TimeSpan.FromMinutes(_sessionSettings.Value.TimeOut));
if (await _cache.GetAsync(item.Key) == null)
_cache.Set(item.Key, item.Ticket, options);
}
_initialized = true;
}
public async Task<string> StoreAsync(AuthenticationTicket ticket)
{
if (!_initialized)
await InitializeAsync();
var key = KeyPrefix + ticket.Principal.Identity.Name;
await RenewAsync(key, ticket);
return key;
}
public async Task RenewAsync(string key, AuthenticationTicket ticket)
{
if (!_initialized)
await InitializeAsync();
if (!key.StartsWith(KeyPrefix))
key = KeyPrefix + key;
var options = new DistributedCacheEntryOptions();
var expiresUtc = ticket.Properties.ExpiresUtc;
if (expiresUtc.HasValue)
{
options.SetAbsoluteExpiration(expiresUtc.Value);
}
options.SetSlidingExpiration(TimeSpan.FromMinutes(_sessionSettings.Value.TimeOut));
var serializedTicket = TicketSerializer.Default.Serialize(ticket);
_cache.Set(key, serializedTicket, options);
using var scope = _serivceProvider.CreateScope();
var persistedTicketService = scope.ServiceProvider.GetRequiredService<IPersistedTicketService>();
var userId = key.Replace(KeyPrefix, "");
var expires = ticket.Properties.ExpiresUtc;
var byteTicket = SerializeToBytes(ticket);
await persistedTicketService.SaveAsync(key, userId, byteTicket, expires);
await persistedTicketService.CommitAsync("System");
}
public async Task<AuthenticationTicket> RetrieveAsync(string key)
{
if (!_initialized)
await InitializeAsync();
if (!key.StartsWith(KeyPrefix))
key = KeyPrefix + key;
AuthenticationTicket ticket = null;
var storedTicket = _cache.Get(key);
if (storedTicket != null)
{
ticket = TicketSerializer.Default.Deserialize(storedTicket);
}
return ticket;
}
public async Task RemoveAsync(string key)
{
if (!_initialized)
await InitializeAsync();
if (!key.StartsWith(KeyPrefix))
key = KeyPrefix + key;
using var scope = _serivceProvider.CreateScope();
var persistedTicketService = scope.ServiceProvider.GetRequiredService<IPersistedTicketService>();
var ticket = await RetrieveAsync(key);
_cache.Remove(key);
await persistedTicketService.RemoveAsync(key);
await persistedTicketService.CommitAsync("System");
}
private byte[] SerializeToBytes(AuthenticationTicket source)
=> TicketSerializer.Default.Serialize(source);
private AuthenticationTicket DeserializeFromBytes(byte[] source)
=> source == null ? null : TicketSerializer.Default.Deserialize(source);
}
}