• ASP.NET MVC View 和 Web API 的基本权限验证


    ASP.NET MVC 5.0已经发布一段时间了,适应了一段时间,准备把原来的MVC项目重构了一遍,先把基本权限验证这块记录一下。

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

    修改Web.config,增加Forms验证模式,在system.web节点中增加以下配置:

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

    【MVC View Controller 篇】

    新建一个PageAuth,继承自AuthorizeAttribute:

    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。

    继承PageBase之后,所有的Controller均需登录,给允许匿名访问的Controller(或Action)增加AllowAnonymous(以AccountController为例):

    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方式提交。

    提交之后,需要开发Web API的接口了。

    【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);
        // 后续其它验证操作
    }
    复制代码

    还没有完成测试该方法,慢慢来吧~~~

  • 相关阅读:
    NO29 用户提权sudo配置文件详解实践--志行为审计
    NO28 第四关考试题
    NO27 定时任务
    NO26 Linux的文件权限--chmod--Linux删除文件说明--suid--sgid
    NO25 三剑客之SED行天下
    NO24 第三关--企业面试题
    gcc编译错误表
    C++的精髓——虚函数
    gcc 优化选项 -O1 -O2 -O3 -Os 优先级,-fomit-frame-pointer
    正确使用#include和前置声明(forward declaration)
  • 原文地址:https://www.cnblogs.com/shengfa/p/4046557.html
Copyright © 2020-2023  润新知