简体   繁体   English

使用Shibboleth进行asp.net MVC身份验证

[英]asp.net MVC authentication with Shibboleth

Shibboleth is a SSO Authentication that is added to IIS as a "plugin". Shibboleth是一种SSO身份验证,作为“插件”添加到IIS中。 After a user has done a Login there are Headers showing the Shibboleth Session: ShibSessionID ShibIdentityProvider eppn affiliation entitlement unscopedaffiliation ...more 在用户完成登录后,会出现显示Shibboleth会话的标题:ShibSessionID ShibIdentityProvider eppn从属关系权利unscopedaffiliation ...更多

So i can extract username and roles from the Headers. 所以我可以从标题中提取用户名和角色。 so far so fine. 到目前为止一切都很好

Question: How can I implement a handler that does read the headers and set the status that a user is authorized? 问题:如何实现读取标头并设置用户授权状态的处理程序? Idea is to use the [Authorize] Attribute and the Method Roles.IsUserInRole. 想法是使用[Authorize]属性和Method Roles.IsUserInRole。 All from the Headers, no Database, no User Management. 全部来自Headers,没有数据库,没有用户管理。

Update 更新

Implementation According to the Answer from @Pharylon 实施根据@Pharylon的回答

In this Update there is nothing new, just a help for the copy&past friends. 在这个更新中没有什么新东西,只是帮助副本和过去的朋友。 Of course you have to adjust the properties and Header fieldnames according to your Shibboleth Setup. 当然,您必须根据Shibboleth设置调整属性和标题字段名称。

File: ShibbolethPrincipal.cs 文件: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; 
        }
    }
}

File: ShibbolethController.cs 文件: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();
            }
        }
    }
}

File: Global.asax 文件:Global.asax

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

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

Using examples: 使用示例:

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

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

You'll want to create a method in Global.asax.cs that has the following signature 您将要在Global.asax.cs中创建具有以下签名的方法

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

This will be called automatically before almost anything else is done (MVC will call this method if it exists, you don't have to "turn it on" anywhere), and this is where you need to set the Principal. 在几乎所有其他操作完成之前,它将被自动调用(MVC将调用此方法,如果它存在,您不必在任何地方“打开”),这是您需要设置Principal的地方。 For instance, let's assume you have a header called RolesHeader that has a comma separated value of roles and another header called UserId that has (duh) the user ID. 例如,假设您有一个名为RolesHeader的标头,该标头具有逗号分隔的角色值,另一个名为UserId标头具有(duh)用户标识。

Your code, without any error handling, might look something like: 您的代码没有任何错误处理,可能看起来像:

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;
}

It's the Principal/Identity that the [Authorize] attribute uses, so setting it here at the beginning of the request lifecycle means the [Authorize] attribute will work correctly. 它是[Authorize]属性使用的Principal / Identity,因此在请求生命周期的开头设置它意味着[Authorize]属性将正常工作。

The rest of this is optional, but I recommend it: 其余部分是可选的,但我建议:

I like to create my own custom classes that implement IPrincipal and IIdentity instead of using the GenericPrincipal and GenericIdentity, so I can stuff more user information in it. 我喜欢创建自己的自定义类来实现IPrincipal和IIdentity,而不是使用GenericPrincipal和GenericIdentity,因此我可以在其中填充更多用户信息。 My custom Principal and Identity objects then have much more rich information, such as branch numbers or email addresses or whatever. 然后,我的自定义Principal和Identity对象具有更丰富的信息,例如分支号码或电子邮件地址等等。

Then, I create a Controller called BaseController that has the following 然后,我创建一个名为BaseController的Controller,它具有以下功能

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

This allows me to access all my rich, custom Principal data instead of just what's defined in IPrincipal. 这允许我访问所有丰富的自定义Principal数据,而不仅仅是IPrincipal中定义的数据。 All of my real controllers then inherit from BaseController instead of directly from Controller . 然后我的所有真实控制器都从BaseController继承,而不是直接从Controller继承。

Obviously, when using a custom Principal like this, in the Application_PostAuthenticateRequest() method, you'd set the Context.User to be your CustomPrincipal instead of a GenericPrincipal . 显然,在使用像这样的自定义Principal时,在Application_PostAuthenticateRequest()方法中,您将Context.User设置为CustomPrincipal而不是GenericPrincipal

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

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