attribute asks user to login instead of access den

2019-04-07 06:53发布

问题:

Updated: Thanks to the help here I've created the following solution:

public class CustomAuthorize : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        // Returns HTTP 401 - see comment in HttpUnauthorizedResult.cs
        // If user is not logged in prompt
        if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
        {
            base.HandleUnauthorizedRequest(filterContext);
        }
        // Otherwise deny access
        else
        {
            filterContext.Result = new RedirectToRouteResult(
                new RouteValueDictionary {
                {"controller", "Account"},
                {"action", "NotAuthorized"}
            });
        }
    }
}

I started from NerdDinner and am using FormsAuthentication with ActiveDirectory as my membership provider. I've added support for roles via my db with Global.asax & AccountController (below).

So now in my controller I have my Authorize attribute set to roles of admin only (below). My logged in user is an author. When I click delete it asks me to login even though I have already done so. Where can I put the logic to return an access denied view?

Global.asax.cs

    protected void Application_AuthenticateRequest(Object sender, EventArgs e)
    {
        HttpCookie authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];
        if (authCookie == null || authCookie.Value == "")
        {
            return;
        }

        FormsAuthenticationTicket authTicket = null;

        try
        {
            authTicket = FormsAuthentication.Decrypt(authCookie.Value);
        }
        catch
        {
            return;
        }

        if (Context.User != null)
        {
            string[] roles = authTicket.UserData.Split(new char[] { ';' });
            Context.User = new GenericPrincipal(Context.User.Identity, roles);
        }
    }

AccountController.cs

    [HttpPost]
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
        Justification = "Needs to take same parameter type as Controller.Redirect()")]
    public ActionResult LogOn(string userName, string password, bool rememberMe, string returnUrl)
    {

        if (!ValidateLogOn(userName, password))
        {
            ViewData["rememberMe"] = rememberMe;
            return View();
        }

        // Make sure we have the username with the right capitalization
        // since we do case sensitive checks for OpenID Claimed Identifiers later.
        userName = this.MembershipService.GetCanonicalUsername(userName);

        // Lookup user's (CWID) appropriate access level
        string accessLevel = userRepository.FindUserByUserName(userName).AccessLevel.LevelName;

        FormsAuthenticationTicket authTicket = new
                        FormsAuthenticationTicket(1, //version
                        userName, // user name
                        DateTime.Now,             //creation
                        DateTime.Now.AddMinutes(30), //Expiration
                        rememberMe, //Persistent
                        accessLevel); // hacked to use roles instead

        string encTicket = FormsAuthentication.Encrypt(authTicket);
        this.Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket));

        if (!String.IsNullOrEmpty(returnUrl))
        {
            return Redirect(returnUrl);
        }
        else
        {
            return RedirectToAction("Index", "Home");
        }
    }

SpotlightController.cs

    [Authorize(Roles="Admin")]
    public ActionResult Delete(int id)

回答1:

Here is what AuthorizeAttribute does, out-of-the-box: It checks to see if the current user is authorized for the current request, and returns HHTP 401/UNAUTHORIZED if they are not, either because they are not logged in at all, or they are not on the list of authorized users/roles for the current request.

The Web forms authentication HTTP module sees this 401 response, intercepts that, and turns it into an HTTP 302 (redirect) response to the login page, if the loginUrl attribute is configured in web.config. The general thought is that if a user is denied access to the site because they are not logged in, but the next thing they will want to do is log in.

Since what you want to do is redirect elsewhere, Hal's suggestion of overriding HandleUnauthorizedRequest and redirecting is not unreasonable. Just bear in mind that if you still want non-authenticated users to see the login page (as opposed to authenticated, but not in the list of permitted users/roles), then you will have to add logic for that. I would advise against the idea of overriding AuthorizeCore or OnAuthorization; neither of these actually solve the problem, and they are much easier to screw up than HandleUnauthorizedRequest.



回答2:

I suppose you could derive from AuthorizeAttribute and then override HandleUnauthorizedRequest and put your own redirect logic there.

Thanks,

Hal



回答3:

Alternatively you can override the Authorize attribute. You would override the OnAuthorization method and get the result from the AuthorizeCore method from your base. Based on that result you can throw an exception, or a custom exception (i.e. Maybe a custom security exception that logs the state) right from within OnAuthirziation.