Ответ 1
Эти методы действительно предназначены для разных целей:
-
IAuthenticationFilter.OnAuthentication
следует использовать для установки принципала, главным из которых является объект, идентифицирующий пользователя.Вы также можете установить результат в этом методе как
HttpUnauthorisedResult
(что избавит вас от выполнения дополнительного фильтра авторизации). Хотя это возможно, мне нравится разделение проблем между различными фильтрами. -
IAuthenticationFilter.OnAuthenticationChallenge
используется для добавления "задачи" к результату до того, как он будет возвращен пользователю.-
Это всегда выполняется непосредственно перед возвратом результата пользователю, что означает, что он может выполняться в разных точках конвейера на разных запросах. См. Объяснение
ControllerActionInvoker.InvokeAction
ниже. -
Использование этого метода для целей "авторизации" (например, проверка того, что пользователь входит в систему или в определенной роли) может быть плохой идеей, так как он может выполняться ПОСЛЕ кода действия контроллера, так что вы, возможно, изменили что-то в db, прежде чем это будет выполнено!
-
Идея состоит в том, что этот метод может использоваться для внесения вклада в результат, а не для выполнения критических проверок полномочий. Например, вы можете использовать его для преобразования
HttpUnauthorisedResult
в перенаправление на разные страницы входа на основе некоторой логики. Или вы можете провести некоторые изменения пользователя, перенаправить его на другую страницу, где вы можете запросить дополнительное подтверждение/информацию, и в зависимости от ответа, наконец, совершите или отмените эти изменения.
-
-
IAuthorizationFilter.OnAuthorization все равно должна использоваться для выполнения проверок проверки подлинности, например, проверка того, входит ли пользователь в систему или принадлежит определенной роли.
Вы можете получить лучшую идею, если вы проверите исходный код ControllerActionInvoker.InvokeAction
. При выполнении действия произойдет следующее:
-
IAuthenticationFilter.OnAuthentication
вызывается для каждого фильтра проверки подлинности. Если директор обновляется в AuthenticationContext, то обновляются какcontext.HttpContext.User
, так иThread.CurrentPrincipal
. -
Если какой-либо фильтр проверки подлинности установил результат, например, установив результат 404, тогда для каждого фильтра проверки подлинности вызывается
OnAuthenticationChallenge
, что позволит изменить результат перед возвратом. (Вы можете, например, преобразовать его в перенаправление на логин). После вызовов результат возвращается, не переходя к шагу 3. -
Если ни один из фильтров проверки подлинности не устанавливает результат, то для каждого
IAuthorizationFilter
выполняется его методOnAuthorization
. -
Как и на шаге 2, если какой-либо фильтр авторизации задал результат, например, установив результат 404, тогда для каждого фильтра проверки подлинности вызывается
OnAuthenticationChallenge
. После вызовов результат возвращается, не переходя к шагу 3. -
Если ни один из фильтров авторизации не установил результат, он продолжит выполнение действия (принимая во внимание проверку запроса и любой фильтр действий)
-
После выполнения действия и до того, как результат будет возвращен, для каждого фильтра проверки подлинности вызывается
OnAuthenticationChallenge
Я скопировал текущий код ControllerActionInvoker.InvokeAction
здесь как ссылку, но вы можете использовать ссылку выше, чтобы увидеть последнюю версию:
public virtual bool InvokeAction(ControllerContext controllerContext, string actionName)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
Contract.Assert(controllerContext.RouteData != null);
if (String.IsNullOrEmpty(actionName) && !controllerContext.RouteData.HasDirectRouteMatch())
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
}
ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext);
ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName);
if (actionDescriptor != null)
{
FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);
try
{
AuthenticationContext authenticationContext = InvokeAuthenticationFilters(controllerContext, filterInfo.AuthenticationFilters, actionDescriptor);
if (authenticationContext.Result != null)
{
// An authentication filter signaled that we should short-circuit the request. Let all
// authentication filters contribute to an action result (to combine authentication
// challenges). Then, run this action result.
AuthenticationChallengeContext challengeContext = InvokeAuthenticationFiltersChallenge(
controllerContext, filterInfo.AuthenticationFilters, actionDescriptor,
authenticationContext.Result);
InvokeActionResult(controllerContext, challengeContext.Result ?? authenticationContext.Result);
}
else
{
AuthorizationContext authorizationContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor);
if (authorizationContext.Result != null)
{
// An authorization filter signaled that we should short-circuit the request. Let all
// authentication filters contribute to an action result (to combine authentication
// challenges). Then, run this action result.
AuthenticationChallengeContext challengeContext = InvokeAuthenticationFiltersChallenge(
controllerContext, filterInfo.AuthenticationFilters, actionDescriptor,
authorizationContext.Result);
InvokeActionResult(controllerContext, challengeContext.Result ?? authorizationContext.Result);
}
else
{
if (controllerContext.Controller.ValidateRequest)
{
ValidateRequest(controllerContext);
}
IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);
ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);
// The action succeeded. Let all authentication filters contribute to an action result (to
// combine authentication challenges; some authentication filters need to do negotiation
// even on a successful result). Then, run this action result.
AuthenticationChallengeContext challengeContext = InvokeAuthenticationFiltersChallenge(
controllerContext, filterInfo.AuthenticationFilters, actionDescriptor,
postActionContext.Result);
InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters,
challengeContext.Result ?? postActionContext.Result);
}
}
}
catch (ThreadAbortException)
{
// This type of exception occurs as a result of Response.Redirect(), but we special-case so that
// the filters don't see this as an error.
throw;
}
catch (Exception ex)
{
// something blew up, so execute the exception filters
ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex);
if (!exceptionContext.ExceptionHandled)
{
throw;
}
InvokeActionResult(controllerContext, exceptionContext.Result);
}
return true;
}
// notify controller that no method matched
return false;
}
Как для того, чтобы не ударить db по каждому запросу при настройке принципала, вы можете использовать какое-то кеширование на стороне сервера.