Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
397 views
in Technique[技术] by (71.8m points)

c# - asp.net MVC authentication with Shibboleth

Shibboleth is a SSO Authentication that is added to IIS as a "plugin". After a user has done a Login there are Headers showing the Shibboleth Session: ShibSessionID ShibIdentityProvider eppn affiliation entitlement unscopedaffiliation ...more

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. All from the Headers, no Database, no User Management.

Update

Implementation According to the Answer from @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.

File: 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

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

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"))
    {
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You'll want to create a method in Global.asax.cs that has the following signature

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. 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.

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.

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. My custom Principal and Identity objects then have much more rich information, such as branch numbers or email addresses or whatever.

Then, I create a Controller called BaseController that has the following

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. All of my real controllers then inherit from BaseController instead of directly from 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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...