简体   繁体   English

编写可扩展的OWIN身份验证

[英]Writing extensible OWIN authentication

I'm working on a set of web services. 我正在开发一组Web服务。 I've implemented a custom OWIN authentication based on my company's security, so now the Authorize attribute works. 我已经基于我公司的安全性实现了自定义OWIN身份验证,因此Authorize属性现在可以使用了。

[Authorize]// It's working!

I want to package it up for others, but I want to support roles first (or at least have a plan/recommendation for how to support roles). 我想将其打包给其他人,但我想首先支持角色(或者至少对如何支持角色有一个计划/建议)。 Unfortunately, my company doesn't have a standard way of handling roles, so different services (or applications) may take different approaches. 不幸的是,我的公司没有处理角色的标准方法,因此不同的服务(或应用程序)可能采用不同的方法。

[Authorize("Member")]// It's not working. :(

What is the best approach to make my authentication middleware easily extensible? 使我的身份验证中间件易于扩展的最佳方法是什么? My particular needs are geared towards supporting roles, but something more generic is fine. 我的特殊需要适合于支持角色,但是更通用的方法也可以。 Some thoughts I've had: 我有一些想法:

  • I could expect them to extend my authentication. 我可以期望他们扩展我的身份验证。
    • This requires more maintenance by the developers. 这需要开发人员进行更多维护。
    • Removes a large chunk of the value in making what I've written reusable. 在使我编写的内容可重复使用时,删除了很大一部分价值。
  • I could provide events to hook into. 我可以提供相关事件。
    • Many of the pre-built authentications seem to do something like this. 许多预建的身份验证似乎都在执行这样的操作。 They accept a provider in their options, and that provider supports a handful of events. 他们在选择中接受提供程序,并且该提供程序支持少数事件。
      • I'm not familiar with this approach; 我对这种方法不熟悉; I'm not sure if it's the best solution. 我不确定这是否是最佳解决方案。
      • My current options object is empty (like many tutorials) and I'm hesitant to change that. 我当前的options对象是空的(就像许多教程一样),我犹豫要更改它。
    • I haven't written (and hardly consumed) any events before, so this would be new territory for me personally. 我以前没有写过(也几乎没写过)任何事件,所以对我个人而言这将是新的领域。

I'm inclined towards the latter approach, but I'm really just shooting in the dark. 我倾向于后一种方法,但实际上我只是在黑暗中拍摄。 I'd greatly appreciate a confident confirmation of what would work or has worked best. 我非常感谢有信心确认什么最有效或最有效。

To support a custom attribute like [Authorize("Member")] you can extend the attributes and write a custom one. 为了支持自定义属性,例如[Authorize(“ Member”)],您可以扩展属性并编写自定义属性。 Extend the System.Web.Http.AuthorizeAttribute class and perform all checks your need to do. 扩展System.Web.Http.AuthorizeAttribute类并执行您需要做的所有检查。

This explains how to do it: 这说明了如何进行:

https://www.codeproject.com/Tips/376810/ASP-NET-WEB-API-Custom-Authorize-and-Exception-Han https://www.codeproject.com/Tips/376810/ASP-NET-WEB-API-Custom-Authorize-and-Exception-Han

And this is an implementation in our solution. 这是我们解决方案中的一个实现。

public class ClaimsAuthorize : System.Web.Mvc.AuthorizeAttribute
    {
        public string PhenixPermissions { get; set; }
        public string[] NeededPermissions;

        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            Logger.Logger.Trace("ClaimsAuthorize.AuthorizeCore");

            NeededPermissions = PhenixPermissions.Split(',');

            if (!(httpContext.User.Identity is ClaimsIdentity))
            {
                return false;
            }

            var claimsIdentity = httpContext.User.Identity as ClaimsIdentity;
            Claim encodedPermissions = claimsIdentity.FindFirst(ConfigurationManager.AppSettings["ApplicationPermissionsKey"]);
            string applicationId = ClaimsHelper.GetClaimValue("audience");

            if (encodedPermissions == null)
            {
                Logger.Logger.Warn("No PhenixPermissions found. Are you logged in?");
                return false;
            }

            var tenant = AppManager.GetApplication(applicationId);

            List<string> decodedPermissions = PermissionsDecodeHelper.BitDecodePermissions(tenant.ConnectionString, encodedPermissions.Value);

            bool hasRights = false;

            foreach (var neededPermission in NeededPermissions)
            {
                if (decodedPermissions.FirstOrDefault(x => x == neededPermission) != null)
                {
                    Logger.Logger.Debug(string.Format("User '{0}' authorized to access view '{1}'.", claimsIdentity.Name.ToString(), httpContext.Request.FilePath));
                    hasRights = true;
                }
            }

            return hasRights;
        }

        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            Logger.Logger.Trace("ClaimsAuthorize.HandleUnauthorizedRequest");

            Logger.Logger.Warn(string.Format("Unauthorized to access '{0}'.", filterContext.Controller));

            filterContext.Controller.TempData["ErrorCode"] = "0123456789";
            filterContext.Controller.TempData["ErrorMessage"] = string.Format("'{0}' is not authorized to access access view '{1}'", HttpContext.Current.User.Identity.Name.ToString(), filterContext.Controller);

            filterContext.Result = new RedirectResult("~/Home/Error");
        }
    }

Some remarks: the PhenixPermissions is the string that comes in when using the attribute like [ClaimsAuthorize(PhenixPermissions = "ConsultExternalLink")] 一些说明:PhenixPermissions是使用[ClaimsAuthorize(PhenixPermissions =“ ConsultExternalLink”)]之类的属性时出现的字符串。

The rest is some specific logic for our application but you should get it. 其余的是我们应用程序的一些特定逻辑,但是您应该了解它。

Just return AuthorizeCore true or false when allowed to call the Controller, API, etc... 当允许调用Controller,API等时,只需将AuthorizeCore返回true或false即可。

Hope this helps 希望这可以帮助

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM