asp.net 使用 Shibboleth 的 MVC 身份验证

asp.net MVC authentication with Shibboleth

Shibboleth 是作为“插件”添加到 IIS 的 SSO 身份验证。 用户完成登录后,Header 显示 Shibboleth Session: ShibSessionID ShibIdentityProvider eppn 联系 权利 未界定的隶属关系 ...更多

所以我可以从 Header 中提取用户名和角色。 到目前为止一切顺利。

问题: 我如何实现一个处理程序来读取 headers 并设置用户被授权的状态? 想法是使用 [授权] 属性和方法 Roles.IsUserInRole。 全部来自 Header 年代,没有数据库,没有用户管理。

更新

根据@Pharylon的回答实现

本次更新没有什么新内容,只是对复制过去的朋友的帮助。 当然,您必须根据您的 Shibboleth 设置调整属性和 Header 字段名。

文件:ShibbolethPrincipal.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Security.Principal; //GenericPrincipal

namespace Shibboleth
{
    public class ShibbolethPrincipal : GenericPrincipal
    {
        public string username
        {
            get { return this.Identity.Name.Replace("@ksz.ch", ""); }
        }

        public string firstname
        {
            get { return HttpContext.Current.Request.Headers["givenName"]; }
        }

        public string lastname
        {
            get { return HttpContext.Current.Request.Headers["surname"]; }
        }

        public string phone
        {
            get { return HttpContext.Current.Request.Headers["telephoneNumber"]; }
        }

        public string mobile
        {
            get { return HttpContext.Current.Request.Headers["mobile"]; }
        }

        public string entitlement
        {
            get { return HttpContext.Current.Request.Headers["eduzgEntitlement"]; }            
        }

        public string homeOrganization
        {
            get { return HttpContext.Current.Request.Headers["homeOrganization"]; }            
        }

        public DateTime birthday
        {
            get
            {
                DateTime dtHappy = DateTime.MinValue;
                try
                {
                    dtHappy = DateTime.Parse(HttpContext.Current.Request.Headers["dateOfBirth"]);
                }
                finally
                {                    
                    
                }

                return dtHappy;
            }
            set {}
        }
        
        public ShibbolethPrincipal()
            : base(new GenericIdentity(GetUserIdentityFromHeaders()), GetRolesFromHeader())
        {
        }

        public static string GetUserIdentityFromHeaders()
        {            
            //return HttpContext.Current.Request.Headers["eppn"];            
            return HttpContext.Current.Request.Headers["principalName"];                        
        }

        public static string[] GetRolesFromHeader()
        {
            string[] roles = null;
            //string rolesheader = HttpContext.Current.Request.Headers["affiliation"];
            string rolesheader = HttpContext.Current.Request.Headers["eduzgEntitlement"];
            if (rolesheader != null)
            {
                roles = rolesheader.Split(';');
            }
            return roles; 
        }
    }
}

文件:ShibbolethController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Shibboleth
{
    public class ShibbolethController : Controller
    {
        protected new ShibbolethPrincipal User
        {
            get
            {
                return (base.User as ShibbolethPrincipal) ?? null; //CustomPrincipal.GetUnauthorizedPrincipal();
            }
        }
    }
}

文件:Global.asax

void Application_PostAuthenticateRequest(object sender, EventArgs e)
        {
            var ctx = HttpContext.Current;

            var principal = new ShibbolethPrincipal();
            HttpContext.Current.User = principal;            
        }

使用示例:

 namespace itservices.Controllers
    {
        [Authorize] //examples : [Authorize(Roles="Administrators")], [Authorize(Users="Alice,Bob")]
        public class PasswordMailController : ShibbolethController
        {

  

    if(User.IsInRole("staff"))
    {

您需要在 Global.asax.cs 中创建一个具有以下签名的方法

protected void Application_PostAuthenticateRequest()
{
    //Your code here.
}

这将在几乎所有其他事情完成之前自动调用(MVC 将调用此方法,如果它存在,您不必 "turn it on" 任何地方),这是您需要设置 Principal 的地方.例如,假设您有一个名为 RolesHeader 的 header,它具有逗号分隔的角色值,另一个名为 UserId 的 header 具有(duh)用户 ID。

您的代码在没有任何错误处理的情况下可能类似于:

protected void Application_PostAuthenticateRequest()
{
    var rolesheader = Context.Request.Headers["RolesHeader"];
    var userId = Context.Request.Headers["UserId"];
    var roles = rolesheader.Split(',');
    var principal = new GenericPrincipal(new GenericIdentity(userId), roles);
    Context.User = principal;
}

[Authorize]属性使用的是Principal/Identity,因此在请求生命周期开始时在这里设置它意味着[Authorize]属性将正常工作。

其余的是可选的,但我推荐它:

我喜欢创建自己的自定义 类 来实现 IPrincipal 和 IIdentity,而不是使用 GenericPrincipal 和 GenericIdentity,因此我可以在其中填充更多用户信息。我的自定义委托人和身份 objects 然后会有更丰富的信息,例如分支机构号码或电子邮件地址等等。

然后,我创建了一个名为 BaseController 的控制器,它具有以下内容

protected new CustomPrincipal User
{
    get
    {
        return (base.User as CustomPrincipal) ?? CustomPrincipal.GetUnauthorizedPrincipal();
    }
}

这使我能够访问我所有丰富的自定义委托人数据,而不仅仅是 IPrincipal 中定义的数据。然后,我所有的真实控制器都继承自 BaseController,而不是直接继承自 Controller

显然,当使用像这样的自定义 Principal 时,在 Application_PostAuthenticateRequest() 方法中,您会将 Context.User 设置为您的 CustomPrincipal 而不是 GenericPrincipal.