10

一部の WCF サービスのフロントエンドとして ASP.NET MVC 4 Web アプリケーションを使用しています。すべてのユーザーのログイン/ログアウトとセッション制御はバックエンドで行われます。MVC アプリは、セッション ID を持つ単一の Cookie のみを保存する必要があります。私のクライアントはフォーム認証の使用を許可していません。すべてをカスタマイズする必要があります。

web.config で次のように設定しました。

  <system.web>
...
    <authentication mode="None" />
  </system.web>

  <system.webServer>
    <modules>
...
      <remove name="FormsAuthentication" />
...    
    </modules>
  </system.webServer>

グローバルフィルターもあります:

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        // Force all actions to request auth. Only actions marked with [AllowAnonymous] will be allowed.
        filters.Add(new MyAuthorizeAttribute());
    }
}

これは Global.asax で呼び出されます

   FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);

承認を必要としないすべてのコントローラーとアクションに [AllowAnonymous] のマークを付けました。

そして今、MyAuthorizeAttribute を実装する必要があります。いくつかのチュートリアルを試しましたが、私のシナリオと完全に一致するものはありません。

基本的に、各アクションに対して次のシナリオを処理する必要があります。

  1. 有効な Cookie がある場合、現在のリクエストは承認されていると見なされます (確認する役割はなく、1 種類のユーザーのみです)。
  2. Cookie がない場合は、デフォルトの MVC ハンドラー (アカウント/ログインをロードしようとする) をオーバーライドし、ユーザーがログインする必要があるというメッセージを表示して、ユーザーをホーム/インデックス ページにリダイレクトする必要があります。
  3. WCF メソッド呼び出しが、カスタム SecurityFault がセッションの有効期限が切れたことを示す FaultException をスローした場合 (SecurityFault には、例外の理由を含むカスタム enum フィールドがあります)、カスタム セッション Cookie を破棄し、ユーザーを Home/Index ページに再度リダイレクトする必要があります。最後のセッションが期限切れになったため、ユーザーがログインする必要があるというメッセージ。他のすべての SecurityFaults については、通過させることができます。グローバル エラー ハンドラがあります。

私が理解している限り、AuthorizeCore (Cookie をチェックして、セッションが存在し、まだ有効かどうかを確認するため) と HandleUnauthorizedRequest (ユーザーをデフォルトのログイン ページではなくホーム/インデックスにリダイレクトするため) をオーバーライドする必要があります。

私が試したリダイレクトのために:

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {            
        base.HandleUnauthorizedRequest(filterContext);
        filterContext.Result = new RedirectResult("/Home/Index/NeedsLogin");
    }

これは、2番目のシナリオをうまく処理しているようです(ただし、そのベースコールについてはわかりません-必要ですか?)。

最初のシナリオでは、AuthorizeCore を実装する必要があります。よくわかりません、正しく行う方法。AuthorizeAttribute には、キャッシュの状況を処理するためのコードと、おそらくもっと多くの隠された機能があることがわかりましたが、それを壊したくありません。

3 番目のシナリオでは、MyAuthorizeAttribute で処理できるかどうかわかりません。AuthorizeAttribute はアクション内で発生する例外をキャッチできますか?それともグローバル エラー ハンドラで SecurityFault.SessionExpired の状況を処理する必要がありますか?

4

3 に答える 3

7

完全にはわかりませんが、 System.Web.MVC.Authorize 属性から継承するカスタム承認フィルターを作成すると、このようになります。

    public class CustomAuthorize : AuthorizeAttribute
    {
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        if (CookieIsValid(filterContext.Request.Cookies["cookieyouwant"])
        {
             filterContext.Result = new RedirectResult("DestUrl");
        }
        else
        {
            filterContext.Result = new RedirectResult("/Home/Index/NeedsLogin");
        }
    }
}

そして、この Authorization を使用する必要があるメソッドを装飾しますか?

于 2012-10-01T21:49:43.373 に答える
3

ここに私が今それをした方法があります:

  public class MyAuthorizeAttribute : AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            bool authorized = false;

            /// MVC 4 boilerplate code follows
            if (filterContext == null)
                throw new ArgumentNullException("filterContext");

            bool skipAuthorization = filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true)
                          || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true);

            if (skipAuthorization)
            {
                return;
            }

            if (OutputCacheAttribute.IsChildActionCacheActive(filterContext))
            {
                throw new InvalidOperationException(
                    "MyAuthorizeAttribute cannot be used within a child action caching block."
                );
            }
            // end of MVC code


            // custom code
            if (!AuthorizeCore(filterContext.HttpContext))
            {
                // if not authorized from some other Action call, let's try extracting user data from custom encrypted cookie
                var identity = MyEncryptedCookieHelper.GetFrontendIdentity(filterContext.HttpContext.Request);
                // identity might be null if cookie not received
                if (identity == null)
                {
                    filterContext.HttpContext.User = new GenericPrincipal(new GenericIdentity(""), null);
                }
                else
                {
                    authorized = true;
                    filterContext.HttpContext.User = new MyFrontendPrincipal(identity);
                }

                // make sure the Principal's are in sync - there might be situations when they are not!
                Thread.CurrentPrincipal = filterContext.HttpContext.User;
            }

            // MVC 4 boilerplate code follows
            if (authorized)
            {
                // ** IMPORTANT **
                // Since we're performing authorization at the action level, the authorization code runs
                // after the output caching module. In the worst case this could allow an authorized user
                // to cause the page to be cached, then an unauthorized user would later be served the
                // cached page. We work around this by telling proxies not to cache the sensitive page,
                // then we hook our custom authorization code into the caching mechanism so that we have
                // the final say on whether a page should be served from the cache.

                HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
                cachePolicy.SetProxyMaxAge(new TimeSpan(0));
                cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */);
            }
            else
            {
                HandleUnauthorizedRequest(filterContext);
            }
            //end of MVC code
        }

        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            if (httpContext == null)
                throw new ArgumentNullException("httpContext");

            // check to make sure the user is authenticated as my custom identity
            var principal = httpContext.User as MyFrontendPrincipal;
            if (principal == null)
                return false;

            var identity = principal.Identity as MyFrontendIdentity;
            if (identity == null)
                return false;

            return true;
        }

        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {            
            // default MVC result was:
            // filterContext.Result = new HttpUnauthorizedResult();

            // but I redirect to index login page instead of kicking 401
            filterContext.Result = new RedirectResult("/Home/Index/NeedsLogin");
        }

        // MVC 4 boilerplate code follows
        private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
        {
            validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
        }

        // This method must be thread-safe since it is called by the caching module.
        protected virtual HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext)
        {
            if (httpContext == null)
                throw new ArgumentNullException("httpContext");

            bool isAuthorized = AuthorizeCore(httpContext);
            return (isAuthorized) ? HttpValidationStatus.Valid : HttpValidationStatus.IgnoreThisRequest;
        }
    } 

ただし、3 番目のシナリオは処理されないため、グローバル エラー ハンドラに実装します。

于 2012-10-02T08:59:54.623 に答える
3

最初の要件について:

すでにわかっているように、OnAuthorizationキャッシュなど、さまざまな側面を処理します。
ユーザー資格情報の検証方法のカスタマイズのみに関心がある場合は、代わりにオーバーライドすることをお勧めしますAuthorizeCore。例えば:

public class ClientCookieAuthorizeAttribute : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        HttpCookie cookie = httpContext.Request.Cookies[_tokenCookieName];

        bool isAuthenticated = ValidateUserByCookie(cookie);

        return isAuthenticated;
    }

    private bool ValidateUserByCookie(HttpCookie cookie)
    {
        var result = false;
        // Perform validation
        // You could include httpContext as well, to check further information
        return result;
    }

    private static const string _tokenCookieName = "myCookieName";
}

この他のスレッドもご覧ください。

  1. SO - カスタム認証属性
  2. ASP.NET - カスタム AuthorizationFilter リダイレクトの問題
  3. 忍者日記
于 2013-03-13T20:14:36.523 に答える