0

4 つのレベルの認証を備えた MVC3 アプリケーションと、それぞれに関連付けられた 4 つの基本コントローラーがあります。

  1. 未認証 -BaseController
  2. ユーザー -BaseAuthController : BaseController
  3. アドバイザー -BaseAdvisorController : BaseAuthController
  4. 管理者 -BaseAdminController : BaseAuthController

現在、特殊なケースのために一連のオーバーライドを用意しています...たとえば、通常は管理者専用のコントローラーに、アドバイザーが使用できる1つまたは2つのアクションメソッドを含めることができます...オーバーライドを配列内の文字列として定義しています。

public class BaseAuthController : BaseController
{
    /// <summary>
    /// Enter action names in here to have them ignored during login detection
    /// </summary>
    public string[] NoAuthActions = new string[] { };

    /// <summary>
    /// Actions only usable by Users+
    /// </summary>
    public string[] UserOnlyActions = new string[] { };

    /// <summary>
    /// Actions only usable by Advisors+
    /// </summary>
    public string[] AdvisorOnlyActions = new string[] { };

    /// <summary>
    /// Actions only usable by Admins+
    /// </summary>
    public string[] AdminOnlyActions = new string[] { };

    .......

    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        //special code here to determine what to do with requested action...
        //verifies that user is logged in and meets requirements for method...
        //if not, redirects out to another page...
    }
}

コントローラーレベルでは、このように定義しています...

public class GrowerController : BaseAdminController
{
    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        UserOnlyActions = new string[] { "GrowthStageSelection" };
        AdvisorOnlyActions = new string[] { "Landing", "SeedSelection", "UpdateProjection",
                                            "NitrogenApplications", "DeleteNitrogen", "MassUpload",
                                            "VerifyHolding", "ConfirmHolding", "DeleteHoldingDir", "DeleteHoldingFile" };
        base.OnActionExecuting(filterContext);
    }

    //......

    [HttpPost]
    public ActionResult GrowthStageSelection(int growerID, int reportGrowthStageID = 0)
    {
        //code...
    }
}

このシステムは実際にはうまく機能していますが、私にとっての問題は、それが面倒に感じることです. メソッドを 1 か所で定義し、必要に応じて別の場所で認証レベルをオーバーライドする必要があります。メソッド名を変更する場合は、他の場所で変更することを忘れないでください。

私ができるようにしたいは、メソッド自体を認証固有の属性で装飾し、文字列ベースの定義を廃止することです (または、少なくともそれらを透明にしてList<string>動的に使用するなど)。これが私が探しているものの例です...

    [HttpPost]
    [AdvisorAuthentication]
    public ActionResult GrowthStageSelection(int growerID, int reportGrowthStageID = 0)
    {
        //code...
    }

問題は、属性でこれを達成する良い方法が見つからないことです。のサブクラスを作成しようとしましたが、それらはのオーバーライドActionFilterAttribute後に実行されます。その時点で、新しいメソッドを文字列リストに動的に追加するには遅すぎます。さらに、属性から現在のコントローラー インスタンスにアクセスすることさえできないようです。BaseAuthControllerOnActionExecuting

たぶん、この全体のアイデアは根拠がありません。誰かが私を正しい方向に向けることができますか? ありがとう。

最終的解決

まず、BaseController を除くすべての特別なコントローラーを削除しました。それらはもう使用できませんでした。現在の特別な認証コードを から に移動しBaseAuthControllerましたBaseController。次に、認証状態ごとに一連の属性を定義しました。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class BaseAuthAttribute : Attribute
{
    public AuthLevels AuthLevel { get; protected set; }

    public BaseAuthAttribute(AuthLevels level)
    {
        this.AuthLevel = level;
    }

    public override string ToString()
    {
        return string.Format("Auth Required: {0}", this.AuthLevel.ToString());
    }
}

public class UnauthenticatedAccess : BaseAuthAttribute
{
    public UnauthenticatedAccess()
        : base(AuthLevels.Unauthenticated)
    {
    }
}

public class UserAccess : BaseAuthAttribute
{
    public UserAccess()
        : base(AuthLevels.User)
    {
    }
}

public class AdvisorAccess : BaseAuthAttribute
{
    public AdvisorAccess()
        : base(AuthLevels.Advisor)
    {
    }
}

public class AdminAccess : BaseAuthAttribute
{
    public AdminAccess()
        : base(AuthLevels.Admin)
    {
    }
}

次に、ログインしているユーザー (存在する場合) の現在の認証レベルを属性に対してチェックするようBaseControllerに変更しました。OnActionExecutingこれで以前よりすっきりしました!(注:SessionUserAuthLevelsはプロジェクトのカスタム オブジェクトです。これらはありません)

public partial class BaseController : Controller
{
    /// <summary>
    /// Override security at higher levels
    /// </summary>
    protected bool SecurityOverride = false;

    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        BaseAuthAttribute authAttribute = filterContext.ActionDescriptor.GetCustomAttributes(false).OfType<BaseAuthAttribute>().FirstOrDefault();
        if (authAttribute == null) //Try to get attribute from controller
            authAttribute = filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(false).OfType<BaseAuthAttribute>().FirstOrDefault();
        if (authAttribute == null) //Fallback to default
            authAttribute = new UnauthenticatedAccess(); //By default, no auth is required for base controller

        if (!SessionUser.LoggedIn
            && authAttribute.AuthLevel == AuthLevels.Unauthenticated)
        {
            SecurityOverride = true;
        }
        else if (SessionUser.LoggedIn
            && SessionUser.LoggedInUser.AuthLevel >= (int)authAttribute.AuthLevel)
        {
            SecurityOverride = true;
        }

        if (!SessionUser.LoggedIn && !SecurityOverride)
        {
            //Send to auth page here...
            return;
        }
        else if (!SecurityOverride)
        {
            //Send somewhere else - the user does not have access to this
            return;
        }

        base.OnActionExecuting(filterContext);
    }

    // ... other code ...
}

それでおしまい!あとはこんな風に使うだけ…

[AdminAccess]
public class GrowerController : BaseController
{
    public ActionResult Index()
    {
        //This method will require admin access (as defined for controller)
        return View();
    }

    [AdvisorAccess]
    public ActionResult Landing()
    {
        //This method is overridden for advisor access or greater
        return View();
    }
}
4

2 に答える 2

1

私があなたの質問を正しく理解していれば、独自のカスタム属性 (承認属性ではない) を実装でき、ベース コントローラーのオーバーライドされた OnActionExecuting で、実行中のメソッドのカスタム属性を取得でき、定義されているものに基づいて適切に取ることができます。行動。したがって、メソッドに [AdvisorAuthentication] がある場合は、続行する前にそれらの資格情報を確認する必要があることがわかります。

編集: これは私のプロジェクトの1つで実装したものであるため、あなたを指す例はありません。私は今そのコードにアクセスできませんが、概要は次のとおりです。

protected override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        base.OnActionExecuted(filterContext);
        IEnumerable<MyCustomAttribute> attributes = filterContext.ActionDescriptor.GetCustomAttributes(false).OfType<MyCustomAttribute>();
        foreach (MyCustomAttributeobj in attributes)
        {
            switch(MyCustomAttribute.AttribType){
                case MyCustomeAttribute.AdvisorAuthentication:

                    break;
                case MyCustomeAttribute.AdminAuthentication:

                    break;
            }
        }
    }

カスタム属性 MyCustomAttribute を 1 つだけ実装して、必要な承認の種類を示すパラメーターを受け入れるようにすることができます。そのような属性の使用は [MyCustomAttribute("MyCustomeAttribute.AdminAuthentication")] になります

于 2012-08-10T09:20:49.393 に答える
0

IAuthorizationFilter and FilterAttributeこのようなものを拡張するさまざまな Authorize 属性を作成できます

public sealed class AuthenticateAdvisorAttribute : IAuthorizationFilter, FilterAttribute
{
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        //advisor specific logic goes here
    }
}

public sealed class AuthenticateAdminAttribute : IAuthorizationFilter, FilterAttribute
{
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        //admin specific logic goes here
    }
}

そして、コントローラーのクラス/アクションに必要な場所にこれらの属性を次のように適用できます。

[AuthenticateAdmin]
public class AdminController : Controller
{

}

[AuthenticateAdvisor]
public class AdvisorController : Controller
{

}
于 2012-08-10T06:07:08.510 に答える