Используйте репозиторий с DbContext в ASP.NET Core Authorize-Attribute: «Не удается получить доступ к удаленному объекту» - PullRequest
0 голосов
/ 19 декабря 2018

Для сторонней аутентификации мне нужен пользовательский атрибут Authorize.Здесь класс репозитория (SessionManager) необходим для проверки, вошел ли пользователь в систему.

[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;
        }
    }
}

В подобном sessionManager.GetCurrentSessionAsync() возникает следующее исключение:

Невозможнополучить доступ к удаленному объекту.Распространенной причиной этой ошибки является удаление контекста, который был разрешен путем внедрения зависимости, а затем попытка использовать тот же экземпляр контекста в другом месте вашего приложения.Это может произойти, если вы вызываете Dispose () для контекста или заключаете контекст в оператор using.Если вы используете внедрение зависимости, вы должны позволить контейнеру введения зависимости позаботиться об удалении экземпляров контекста.Имя объекта: 'AsyncDisposer'.

Я знаю об этом и не могу ничего утилизировать самостоятельно.VBSessionManager получил мой DbContext в своем конструкторе.Внутри GetCurrentSessionAsync куки были проверены с помощью запросов к базе данных LinQ.Так что не нужно вызывать директивы Dispose, using или что-то в этом роде.

Инъекция в 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>();

Ответы [ 4 ]

0 голосов
/ 19 декабря 2018
public class VBAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
    public async void OnAuthorization(AuthorizationFilterContext context)
    {
        // …

        await something;

        // …
    }
}

Метод async void - это почти всегда плохая идея .Асинхронные методы должны возвращать Task, чтобы вызывающие могли определить результат асинхронного процесса.

Поскольку вы реализуете IAuthorizationFilter, вы реализуете синхронный фильтр авторизации.Вы используете это, когда вам не нужно делать что-то асинхронно.Это, например, верно, если вам просто нужно посмотреть на некоторые параметры, а затем получить какое-то решение, чтобы определить, разрешен ли доступ.

Если вам требуются асинхронные процессы, вы должны не сделать метод void асинхронным, но вместо этого реализовать IAsyncAuthorizationFilter.Это интерфейс для реализации асинхронного фильтра авторизации.В этом случае метод, который вам нужно реализовать, выглядит немного иначе:

Task OnAuthorizationAsync(AuthorizationFilterContext context)

Как видите, этот метод возвращает Task, поэтому он может правильно выполнять асинхронные процессы.В вашем случае, когда вы хотите await что-то внутри метода, вы можете просто сделать это:

public class VBAuthorizeAttribute : AuthorizeAttribute, IAsyncAuthorizationFilter
{
    public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
    {
        // …

        await something;

        // …
    }
}

Теперь, с надлежащим асинхронным методом, который возвращает Task, вызывающая система будетбыть в состоянии правильно использовать метод, и продолжение обработки запроса будет ожидать обработки вашего фильтра авторизации.

0 голосов
/ 19 декабря 2018

Метод OnAuthorization не должен использоваться для проверки авторизации.Это просто уведомление, что «эй, авторизация происходит сейчас».

Тем не менее, некоторые использовали его для этого .Но так как вы объявили его как async void, ничто не ждет завершения этого метода.Это корень вашего исключения: к моменту вызова базы данных запрос уже завершен, и контекст удаляется.Вы можете просто удалить async ....

Но правильное решение - использовать IAuthorizationHandler, который предназначен, как следует из названия,разрешение на обработку.У него есть метод HandleAsync, который является правильным методом async, который действительно ожидается (он ожидает вашего решения по авторизации, прежде чем продолжить).

Посмотрите на этот ответ от сотрудника Microsoft.Вы настраиваете обработчик, затем используете его с обычным AuthorizeAttribute, например так:

[Authorize(Policy = "MyCustomPolicy")]
0 голосов
/ 19 декабря 2018
public async void OnAuthorization(AuthorizationFilterContext context) {

Важным здесь является использование async void, то есть по Дэвиду Фаулеру , ВСЕГДА плохо.При имеющейся здесь настройке сам вызов OnAuthorization не может быть await ed, что означает, что происходит что-то вроде следующего:

  1. Экземпляры Scoped VBSessionManager и VBDbContextсоздаются за некоторое время до вызова метода OnAuthorization.
  2. Ваш OnAuthorization выполняет и вызывает VBSessionManager.GetCurrentSessionAsync, возвращаясь до того, как указанный метод имеет возможность завершить (из-за использованияasync / await).
  3. Когда OnAuthorization завершено, IDisposable -элемент VBDbContext удаляется.
  4. Код внутри VBSessionManager.GetCurrentSessionAsync все еще выполняется -он пытается использовать экземпляр VBDbContext, который был удален.

Причина, по которой async void используется в вашей ситуации, заключается просто в том, что это то, что объявлено в IAuthorizationFilter интерфейс - вы хотите использовать await, и единственный способ сделать это - пометить ваш метод реализации как async (вы не можете сделать его async Task, потому что это не будет реализовывать интерфейс).

С точки зрения решения этой проблемы я бы согласился сh Габриэль Люси, что использование авторизации на основе политик будет правильным решением.

0 голосов
/ 19 декабря 2018

Кажется, что использование async вызывает проблемы.Когда я изменяю OnAuthorization на метод синхронизации, подобный этому, я не получаю никаких ошибок:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
    public class VBAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter {
        public void OnAuthorization(AuthorizationFilterContext context) {
            var sessionManager = (VBSessionManager)context.HttpContext.RequestServices.GetService(typeof(VBSessionManager));
            var user = sessionManager.GetCurrentSessionAsync().Result;
            if (user == null) {
                context.Result = new UnauthorizedResult();
                return;
            }
        }
    }

Не знаю, не разработаны ли эти атрибуты (или, возможно, только AuthorizeAttribute)работать асинхронно.Для меня текущее решение - использовать метод syn.Я также считаю, что это не должно снижать производительность.Но если кто-то знает о фонах и даже имеет представление о том, как мы можем использовать атрибут async, я буду рад другому ответу.

...