看来,他们中的任何一个(OnAuthentication或OnAuthenticationChallenge)都可以执行身份验证所需的所有操作.为什么需要2种方法?
我的理解是OnAuthentication是我们将逻辑逻辑(或者这个逻辑应该在实际操作方法中),连接到数据存储并检查用户帐户. OnAuthenticationChallenge是我们重定向到登录页面的地方,如果不通过验证.它是否正确?为什么我不能重定向OnAuthentication并且不实现OnAuthenticationChallenge.我知道有一些我失踪的东西有人可以向我解释吗?
存储已验证用户的最佳做法是什么,以便后续请求不必连接到db再次检查用户?
请记住,我是ASP.NET MVC的新手.
解决方法
> 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所示,如果任何授权筛选器设置结果,则会为每个验证筛选器调用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,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,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,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,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; }
至于在设置主体时,在每次请求时都不会碰到数据库,可以使用某种服务器端缓存.