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

Posted

技术标签:

【中文标题】使用 Shibboleth 的 asp.net MVC 身份验证【英文标题】:asp.net MVC authentication with Shibboleth 【发布时间】:2015-04-27 09:17:49 【问题描述】:

Shibboleth 是作为“插件”添加到 IIS 的 SSO 身份验证。 用户完成登录后,会出现显示 Shibboleth 会话的标题: ShibSessionID ShibIdentityProvider eppn 联系 权利 无范围的从属关系 ...更多

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

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

更新

根据@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"))
    

【问题讨论】:

您是否尝试创建一个动作过滤器并在 OnActionExecuting 方法中进行此处理? msdn.microsoft.com/en-us/library/dd381609%28v=vs.100%29.aspx 我认为最好在开始处理请求时设置主体/身份,以便正常的授权过滤器和方法工作。 @RazvanDumitru - ActionFilters 执行 AFTER 授权过滤器,它们没有用 很高兴我能帮上忙。不过,我建议 ShibbolethController 的 User 永远不要返回 null 对象。您不希望 User.Identity.IsAuthenticated 引发异常 - 它应该是检查用户是否有效的可靠方法。此外,您应该在实例化时设置 Principal 的值(我喜欢将其设为不可变对象)。否则,如果你让单元测试不断调用 HttpContext.Current,你将很难进行单元测试。 你好法里伦。朱普,有道理。我也在为 UnauthorizedPrincipal 进行更改。谢谢 【参考方案1】:

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

protected void Application_PostAuthenticateRequest()

    //Your code here.

这将在几乎所有其他操作完成之前自动调用(如果存在,MVC 将调用此方法,您不必在任何地方“打开”它),这是您需要设置 Principal 的地方。例如,假设您有一个名为 RolesHeader 的标头具有逗号分隔的角色值和另一个名为 UserId 的标头具有(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,因此我可以在其中填充更多用户信息。然后,我的自定义 Principal 和 Identity 对象具有更丰富的信息,例如分支机构编号或电子邮件地址等。

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

protected new CustomPrincipal User

    get
    
        return (base.User as CustomPrincipal) ?? CustomPrincipal.GetUnauthorizedPrincipal();
    

这允许我访问我所有丰富的自定义 Principal 数据,而不仅仅是 IPrincipal 中定义的数据。然后我所有的真实控制器都从BaseController 继承,而不是直接从Controller 继承。

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

【讨论】:

太棒了!这比我预期的要容易得多。我担心我必须实现 ExtendetMembershibProvider 和 RoleProvider。非常感谢

以上是关于使用 Shibboleth 的 asp.net MVC 身份验证的主要内容,如果未能解决你的问题,请参考以下文章

将 WordPress iOS 应用程序与受 Shibboleth 保护的 WordPress 配合使用

了解 Shibboleth 和 SAML

Shibboleth 和 .NET

使用 python 登录 SAML/Shibboleth 认证服务器

模型没有在 asp.net mvc 中使用 ajax post 更新?

Shibboleth