Per l'autenticazione di terze parti, ho bisogno di un attributo Authorize
personalizzato. Qui è richiesta una classe repository ( SessionManager
) per verificare se l'utente è loggato.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class VBAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter {
public async void OnAuthorization(AuthorizationFilterContext context) {
var sessionManager = (VBSessionManager)context.HttpContext.RequestServices.GetService(typeof(VBSessionManager));
var user = await sessionManager.GetCurrentSessionAsync();
if (user == null) {
context.Result = new UnauthorizedResult();
return;
}
}
}
Nella sessionManager.GetCurrentSessionAsync()
verifica la seguente eccezione:
Impossibile accedere a un oggetto disposto. Una causa comune di questo errore è l'eliminazione di un contesto risolto dall'integrazione delle dipendenze e successivamente il tentativo di utilizzare la stessa istanza di contesto altrove nell'applicazione. Ciò può verificarsi se si chiama Dispose () nel contesto o si avvolge il contesto in un'istruzione using. Se si sta utilizzando l'integrazione delle dipendenze, è necessario lasciare che il contenitore di input delle dipendenze si occupi di eliminare le istanze di contesto. Nome oggetto: 'AsyncDisposer'.
Sono consapevole di questo e non mi disprezzo da solo. VBSessionManager
ha iniettato il mio DbContext
nel suo costruttore. All'interno di GetCurrentSessionAsync
cookie di GetCurrentSessionAsync
sono stati controllati con le query del database LinQ. Quindi nessuna chiamata di Dispose
, using
direttive o qualcosa del genere.
VBSessionManager
public class VBSessionManager {
readonly VBDbContext db;
readonly IHttpContextAccessor contextAccessor;
const string sessionHashCookieName = "xxx";
VBSession currentSession;
public VBSessionManager(VBDbContext db, IHttpContextAccessor contextAccessor) {
this.db = db;
this.contextAccessor = contextAccessor;
}
public async Task<VBSession> GetCurrentSessionAsync() {
if (currentSession == null) {
string sessionCookie = GetCookieWithoutPrefix(sessionHashCookieName);
currentSession = await GetSessionAsync(sessionCookie);
if (currentSession == null) {
var cookieUser = GetUserFromCookiePassword().Result;
// No session detected
if (cookieUser == null) {
return null;
}
currentSession = db.Sessions.FirstOrDefault(s => s.UserId == cookieUser.Id);
}
}
return currentSession;
}
// ...
}
services.AddDbContext<VBDbContext>(options => {
string connectionString = Configuration.GetValue<string>("VBConnectionString");
options.UseMySql(connectionString,
mySqlOptions => {
mySqlOptions.ServerVersion(new Version(10, 2, 19), ServerType.MariaDb);
}
);
bool isDev = CurrentEnvironment.IsDevelopment();
options.EnableSensitiveDataLogging(isDev);
});
services.AddScoped<VBSessionManager>();
public class VBAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
public async void OnAuthorization(AuthorizationFilterContext context)
{
// …
await something;
// …
}
}
Avere un metodo async void
è quasi sempre una cattiva idea . Metodi asincroni dovrebbero restituire un Task
per rendere chiamanti in grado di determinare il risultato del processo asincrono.
Poiché si sta implementando IAuthorizationFilter
, si implementa un filtro di autorizzazione sincrono . Lo usi quando non hai bisogno di fare qualcosa in modo asincrono. Questo è vero per esempio se hai solo bisogno di guardare alcuni dei parametri e poi avere qualche ruling per determinare se l'accesso è consentito o meno.
Se avete bisogno di processi asincroni, non si dovrebbe fare il void
metodo asincrono ma invece implementare IAsyncAuthorizationFilter
. Questa è l'interfaccia per implementare un filtro di autorizzazione asincrono . In tal caso, il metodo che devi implementare ha un aspetto leggermente diverso:
Task OnAuthorizationAsync(AuthorizationFilterContext context)
Come si può vedere, questo metodo restituisce un Task
in modo che possa correttamente fare processi asincroni. Nel tuo caso, dove vuoi await
qualcosa all'interno del metodo, puoi semplicemente farlo:
public class VBAuthorizeAttribute : AuthorizeAttribute, IAsyncAuthorizationFilter
{
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
// …
await something;
// …
}
}
Ora, con un adeguato metodo asincrono che restituisce un Task
, il sistema di chiamata sarà in grado di consumare il metodo corretto e la continuazione del trattamento richiesta attenderà per il filtro di autorizzazione da elaborare.