Azure - 實作Azure上的AD與MVC進行驗證 - Azure設定篇 ( 三 )

03 September 2014 — Written by Sky Chang
#Active Directory#ASP.NET MVC#Azure

持續努力,現在進入第三篇了!!,第三篇我們先以完成為目標,所以小弟先不在這邊講Code的內容了,這部份未來再補上。

image

TenantDbContext.cs的內容為:

using System;
using System.Data.Entity;

namespace WebApplication2.Models
{
    public class TenantDbContext : DbContext
    {
        public TenantDbContext()
            : base("DefaultConnection")
        {
        }

        public DbSet<IssuingAuthorityKey> IssuingAuthorityKeys { get; set; }

        public DbSet<Tenant> Tenants { get; set; }
    }
}

TenantRegistrationModels.cs的內容為:

using System;
using System.Collections.Generic;
using System.Linq;

namespace WebApplication2.Models
{
    public class IssuingAuthorityKey
    {
        public string Id { get; set; }
    }

    public class Tenant
    {
        public string Id { get; set; }
    }
}

接著,我們要建立一個Utils的目錄,然後建立一個DatabaseIssuerNameRegistry.cs檔案。

image

DatabaseIssuerNameRegistry.cs的內容為:

using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Web;
using System.Web.Hosting;
using System.Xml.Linq;
using WebApplication2.Models;

namespace WebApplication2.Utils
{
    public class DatabaseIssuerNameRegistry : ValidatingIssuerNameRegistry
    {
        public static bool ContainsTenant(string tenantId)
        {
            using (TenantDbContext context = new TenantDbContext())
            {
                return context.Tenants
                    .Where(tenant => tenant.Id == tenantId)
                    .Any();
            }
        }

        public static bool ContainsKey(string thumbprint)
        {
            using (TenantDbContext context = new TenantDbContext())
            {
                return context.IssuingAuthorityKeys
                    .Where(key => key.Id == thumbprint)
                    .Any();
            }
        }

        public static void RefreshKeys(string metadataLocation)
        {
            IssuingAuthority issuingAuthority = ValidatingIssuerNameRegistry.GetIssuingAuthority(metadataLocation);

            bool newKeys = false;
            bool refreshTenant = false;
            foreach (string thumbprint in issuingAuthority.Thumbprints)
            {
                if (!ContainsKey(thumbprint))
                {
                    newKeys = true;
                    refreshTenant = true;
                    break;
                }
            }

            foreach (string issuer in issuingAuthority.Issuers)
            {
                if (!ContainsTenant(GetIssuerId(issuer)))
                {
                    refreshTenant = true;
                    break;
                }
            }

            if (newKeys || refreshTenant)
            {
                using (TenantDbContext context = new TenantDbContext())
                {
                    if (newKeys)
                    {
                        context.IssuingAuthorityKeys.RemoveRange(context.IssuingAuthorityKeys);
                        foreach (string thumbprint in issuingAuthority.Thumbprints)
                        {
                            context.IssuingAuthorityKeys.Add(new IssuingAuthorityKey { Id = thumbprint });
                        }
                    }

                    if (refreshTenant)
                    {
                        foreach (string issuer in issuingAuthority.Issuers)
                        {
                            string issuerId = GetIssuerId(issuer);
                            if (!ContainsTenant(issuerId))
                            {
                                context.Tenants.Add(new Tenant { Id = issuerId });
                            }
                        }
                    }
                    context.SaveChanges();
                }
            }
        }

        private static string GetIssuerId(string issuer)
        {
            return issuer.TrimEnd('/').Split('/').Last();
        }

        protected override bool IsThumbprintValid(string thumbprint, string issuer)
        {
            return ContainsTenant(GetIssuerId(issuer))
                && ContainsKey(thumbprint);
        }
    }
}

接下來,我們要在App_Start底下新增IdentityConfig.cs檔案。

image

IdentityConfig.cs的內容為:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.IdentityModel.Claims;
using System.IdentityModel.Services;
using System.Linq;
using System.Web.Helpers;
using WebApplication2.Utils;

namespace WebApplication2
{
    // 如需 ASP.NET Identity 的詳細資訊,請造訪 http://go.microsoft.com/fwlink/?LinkId=301863
    public static class IdentityConfig
    {
        public static string AudienceUri { get; private set; }
        public static string Realm { get; private set; }

        public static void ConfigureIdentity()
        {
            RefreshValidationSettings();
            // 設定應用程式領域
            Realm = ConfigurationManager.AppSettings["ida:realm"];

            // 設定應用程式 audienceUri
            AudienceUri = ConfigurationManager.AppSettings["ida:AudienceUri"];
            if (!String.IsNullOrEmpty(AudienceUri))
            {
                UpdateAudienceUri();
            }

            AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name;
        }

        public static void RefreshValidationSettings()
        {
            string metadataLocation = ConfigurationManager.AppSettings["ida:FederationMetadataLocation"];
            DatabaseIssuerNameRegistry.RefreshKeys(metadataLocation);
        }

        public static void UpdateAudienceUri()
        {
            int count = FederatedAuthentication.FederationConfiguration.IdentityConfiguration
                .AudienceRestriction.AllowedAudienceUris.Count(
                    uri => String.Equals(uri.OriginalString, AudienceUri, StringComparison.OrdinalIgnoreCase));
            if (count == 0)
            {
                FederatedAuthentication.FederationConfiguration.IdentityConfiguration
                    .AudienceRestriction.AllowedAudienceUris.Add(new Uri(IdentityConfig.AudienceUri));
            }
        }
    }
}

接下來,我們要在Global.asax裡面加入註冊使用上面的IdentityConfig。

image

加入IdentityConfig.ConfigureIdentity();

void Application_Start(object sender, EventArgs e)
{
    // 應用程式啟動時執行的程式碼
    AreaRegistration.RegisterAllAreas();
    IdentityConfig.ConfigureIdentity();
    GlobalConfiguration.Configure(WebApiConfig.Register);
    RouteConfig.RegisterRoutes(RouteTable.Routes);            
}

最後,我們有用到DB,所以還是要補一下連線字串;這邊小弟我是使用LocalDB,大家也可以建立自己想要的DB位置。

  <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\aspnet-WebApplication1-20140901114034.mdf;Initial Catalog=aspnet-WebApplication1-20140901114034;Integrated Security=True" providerName="System.Data.SqlClient" />
  </connectionStrings>

完成之後,你會發現,使用HTTPS時,就會要求先使用Azure來登入帳號了~~

image

以上!!

參考資料

Sky & Study4.TW