ASP.NET MVC View 和 Web API 的基本權限驗證

ASP.NET MVC 5.0已經發布一段時間了,適應了一段時間,準備把原來的MVC項目重構了一遍,先把基本權限驗證這塊記錄一下。web

環境:Windows 7 Professional SP1 + Microsoft Visual Studio 2013(MVC 5 + Web API 2)安全

修改Web.config,增長Forms驗證模式,在system.web節點中增長如下配置:cookie

<authentication mode="Forms">
  <forms loginUrl="~/login" defaultUrl="~/" protection="All" timeout="20" name="__auth" />
</authentication>

【MVC View Controller 篇】ide

新建一個PageAuth,繼承自AuthorizeAttribute:post

using System;
using System.Net;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class PageAuth : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        if (httpContext == null)
        {
            return false;
        }

        if (httpContext.User.Identity.IsAuthenticated && base.AuthorizeCore(httpContext))
        {
            return ValidateUser();
        }

        httpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
        return false;
    }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);

        if (filterContext.HttpContext.Response.StatusCode == (int)HttpStatusCode.Forbidden)
        {
            filterContext.Result = new RedirectToRouteResult("AccessErrorPage", null);
        }
    }

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        filterContext.HttpContext.Response.Redirect(FormsAuthentication.LoginUrl);
    }

    private bool ValidateUser()
    {
        //TODO: 權限驗證
        return true;
    }
}

建一個Controller的基類PageBase,繼承自Controller:測試

using System.Web.Mvc;
[PageAuth]
public class PageBase : Controller
{
}

全部View的Controller均繼承自PageBase,再也不繼承自Controller。spa

繼承PageBase以後,全部的Controller均需登陸,給容許匿名訪問的Controller(或Action)增長AllowAnonymous(以AccountController爲例):code

using System.Web.Mvc;
public class AccountController : PageBase
{
    [AllowAnonymous]
    public ActionResult Login()  // 可匿名訪問
    {
        ViewBag.Title = "用戶登陸";
        return View();
    }

    public ActionResult Detail(int id)   // 需登陸訪問
    {
        ViewBag.Title = "用戶詳情";
        return View();
    }
}

頁面Controller的開發,基本結束,接下來就是在登陸頁面(~/login)使用js提交登陸信息,用post方式提交。orm

提交以後,須要開發Web API的接口了。blog

【MVC Web API Controller 篇】

一樣,新建一個ApiAuth,繼承自ActionFilterAttribute:

using System;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using System.Web.Security;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class ApiAuth : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        try
        {
            if (actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Count > 0)   // 容許匿名訪問
            {
                base.OnActionExecuting(actionContext);
                return;
            }

            var cookie = actionContext.Request.Headers.GetCookies();
            if (cookie == null || cookie.Count < 1)
            {
                actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);
                return;
            }

            FormsAuthenticationTicket ticket = null;

            foreach (var perCookie in cookie[0].Cookies)
            {
                if (perCookie.Name == FormsAuthentication.FormsCookieName)
                {
                    ticket = FormsAuthentication.Decrypt(perCookie.Value);
                    break;
                }
            }

            if (ticket == null)
            {
                actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);
                return;
            }

            // TODO: 添加其它驗證方法

            base.OnActionExecuting(actionContext);
        }
        catch
        {
            actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);
        }
    }
}

新建一個ApiController的基類ApiBase,繼承自ApiController:

using System.Web.Http;
[ApiAuth]
public class ApiBase : ApiController
{
}

全部API的Controller均繼承自ApiBase,再也不繼承自ApiController。

繼承ApiBase以後,給容許匿名訪問的Controller(或Action)增長AllowAnonymous(以LoginController爲例):

using System.Web.Http;
using System.Web.Security;
public class LoginController : ApiBase
{
    [HttpPost]
    [AllowAnonymous]
    public bool Login([FromBody]LoginInfo loginInfo)
    {
        try
        {
            var cookie = FormsAuthentication.GetAuthCookie("Username", false);
            var ticket = FormsAuthentication.Decrypt(cookie.Value);
            var newTicket = new FormsAuthenticationTicket(ticket.Version, ticket.Name, ticket.IssueDate, ticket.Expiration, ticket.IsPersistent, "");
            cookie.Value = FormsAuthentication.Encrypt(newTicket);
            DeyiContext.Response.Cookies.Add(cookie);return true;
        }
        catch
        {
            return false;
        }
    }
}

【寫在最後】

網上查了不少方法,還須要時間驗證一下各個方法的合理度。

關於Web API的安全性,我的以爲,仍是採用SSL的方式更加穩妥一些。

另外,網上不少寫的在Web API的權限判斷的時候,使用的是actionContext.Request.Headers.Authorization來判斷,以下:

if (actionContext.Request.Headers.Authorization == null)
{
    // 判斷是否容許匿名訪問
}
else
{
    var ticket = FormsAuthentication.Decrypt(actionContext.Request.Headers.Authorization.Parameter);
    // 後續其它驗證操做
}

尚未完成測試該方法,慢慢來吧~~~

相關文章
相關標籤/搜索