65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Azure Active Directory 和 MVC 进行基于角色的访问控制

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2014年4月2日

CPOL

5分钟阅读

viewsIcon

43473

downloadIcon

5

在 ASP.NET MVC 应用程序中集成基于角色的访问控制与 Azure Active Directory Graph API

引言

许多应用程序和网站需要控制特定用户组对某些区域的访问权限,例如,信用控制用户可以看到信用历史记录,而订单处理人员只需要查看他们的账户是否有信用,并且可以下单。

这通常通过以下方法之一实现:

  • 调用 IPrincipal.IsInRole(role) 方法。
  • 在类或方法上放置 [PrincipalPermission(user, role)] 属性。
  • 创建 PrincipalPermission(user, role) 实例并调用 Demand()

通过 Active Directory 或数据库使用 ASP.NET 角色提供程序获取组,有关更多信息,请参见此处:http://msdn.microsoft.com/en-us/library/aa478950.aspx

如果您想利用 Azure Active Directory 并使用 Active Directory 组来实现基于角色的身份验证,那么这并不是原生支持的,您需要查询“Azure Active Directory Graph API”。

有关 Azure Active Directory 及其优势的更多信息,您可以查看 Azure 网站

接下来,我们将讨论如何使用 nuget 包 Azure.ActiveDirectory 为我们的应用程序轻松添加角色身份验证。

背景

默认情况下,Visual Studio 项目向导将项目设置为使用 Entity Framework 作为 ValidatingIssuerNameRegistry,由于 Azure 中的 SQL 存储成本较高,我们将改用成本极低的表存储。随着时间的推移,我希望改进 nuget 体验,以减少所需的手动步骤。本文的其余部分假设您拥有 Azure 帐户和已设置的 Active Directory,或者您可以从管理门户轻松创建一个新的。

在管理门户中,我在目录 codeproject.onmicrosoft.com 中创建了 2 个用户和 2 个组。

  • admin@codeproject.onmicrosoft.com 作为“Admin”组的成员,并拥有“全局管理员角色”,这将允许我们在运行 Visual Studio 向导时在 Active Directory 中创建应用程序。
  • member@codeproject.onmicrosoft.com 作为“Member”组的成员。
我们将使用这些用户来测试我们在 MVC 示例应用程序中的角色身份验证是否正常工作。

使用代码

创建一个新的 ASP.NET 项目,选择 MVC 项目模板,然后选择“更改身份验证”按钮来配置 MVC 项目以使用我们的 Active Directory。由于我们要启用 Azure Graph API 来返回我们的组,因此我们需要确保已选择允许读取目录数据的选项。

我输入了我的 Active Directory 名称 codeproject.onmicrosoft.com,并选择了“单一登录,读取目录数据”以允许访问 Graph。

应用程序 ID URI 用于在 Azure 目录中标识您的应用程序,默认情况下,它会从您的域名和项目名称自动生成。

点击“确定”后,使用您的管理员用户登录。

Visual Studio 创建项目后,您需要在程序包管理器控制台中运行以下命令。

  • Install-Package Azure.ActiveDirectory
  • Install-Package Microsoft.WindowsAzure.ConfigurationManager (出于某种原因,没有添加 nuget 依赖项)
  • Update-Package (可选,更新到最新版本)

现在我们需要对 web.config 进行一些修改,以注册我们的存储帐户和 AzureTableIssuerNameRegistryAzureTableIssuerNameRegistry 在 Azure 表存储上实现了 ValidatingIssuerNameRegistry,而不是默认注册的 Entity Framework 提供程序。

配置 IssuerNameRegistry

默认情况下,配置将指向向导在应用程序中创建的 DatabaseIssuerNameRegistry。

  <system.identityModel>
    <identityConfiguration>
      <issuerNameRegistry type="RolesBasedAuthenticationSample.Utils.DatabaseIssuerNameRegistry, RolesBasedAuthenticationSample" />
      <audienceUris>
        <add value="https://codeproject.onmicrosoft.com/RolesBasedAuthenticationSample" />
      </audienceUris>
    <!--
       rest of details ommited
       ..
    -->
  </system.identityModel>

更新 issuerNameRegistry 以指向包注册表。

<issuerNameRegistry type="Azure.ActiveDirectory.AzureTableIssuerNameRegistry, Azure.ActiveDirectory" />  

现在我们已经注册了新的注册表,可以删除项目中的以下文件,因为它们不再需要了。

  • Models\TenantDbContext.cs
  • Models\TenantReistrationModels.cs
  • Models\HomeViewModel.cs
  • Utils\DatabaseIssuerNameRegistry

为了使 AzureTableIssuerNameRegistry 工作,我们需要将 appSettings 或 CloudSettings 配置键 Identity.StorageConnectionString 设置为存储连接字符串,它应该类似于:

<add key="Identity.StorageConnectionString" value="DefaultEndpointsProtocol=https;AccountName=[name];AccountKey=[key]" /> 

您的存储帐户的正确连接字符串可以在 Azure 管理门户或 Visual Studio 的服务器资源管理器属性面板中轻松找到。

更新 IdentityConfig.cs

需要更新 App_Start 文件夹下的 IdentityConfig,以便从 Azure 刷新其密钥,而不是默认的 EF 注册表。更新 RefreshValidationSettings() 方法。

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

ConfigureIdentity() 方法中,我们需要告诉身份配置从 Azure Graph 获取我们的角色声明。我们通过添加以下行来实现:

 FederatedAuthentication.FederationConfiguration.IdentityConfiguration.ClaimsAuthenticationManager = new Azure.ActiveDirectory.GraphClaimsAuthenticationManager(); 

现在,我们应该可以按 F5 运行应用程序并登录了。当出现提示时,我们将成功登录并看到默认站点。

HomeController.cs

默认情况下,HomeController 声明了许多变量,并向 Graph API 发出请求,该请求返回 JSON,然后将 JSON 反序列化为之前删除的 UserProfile 类,该类包含在 HomeViewModel.cs 中。

我们将更新 HomeController 以使用 IActiveDirectoryGraphClient,并将当前用户传递给 UserProfile 视图。

using System.Web.Mvc;
using Azure.ActiveDirectory;

namespace RolesBasedAuthenticationSample.Controllers
{
    public class HomeController : Controller
    {
        private readonly IActiveDirectoryGraphClient _graphClient = new GraphClient();

        [Authorize]
        public ActionResult UserProfile()
        {
            return View(_graphClient.CurrentUser);
        }

        public ActionResult Index()
        {
            return View();
        }

        public ActionResult About()
        {
            ViewBag.Message = "Your application description page.";

            return View();
        }

        public ActionResult Contact()
        {
            ViewBag.Message = "Your contact page.";

            return View();
        }
       
    }
} 
UserProfie.cshtml

由于我们不再使用 ASP.NET 项目中创建的 UserProfile,我们需要快速更新视图以使用我们的 User 对象。

@model Azure.ActiveDirectory.AzureGraphService.Microsoft.WindowsAzure.ActiveDirectory.User
@{
    ViewBag.Title = "User Profile";
}

<h2>@ViewBag.Title.</h2>

<table class="table table-bordered table-striped">
    <tr>
        <td>Display Name</td>
        <td>@Html.DisplayFor(m=>m.displayName)</td>
    </tr>
    <tr>
        <td>First Name</td>
        <td>@Html.DisplayFor(m=>m.givenName)</td>
    </tr>
    <tr>
        <td>Last Name</td>
        <td>@Html.DisplayFor(m=>m.surname)</td>
    </tr>
</table>  

就是这样,当您运行应用程序时,您应该仍然看到上面的屏幕。请注意,该包包含一些数据注释来提供列名。

添加角色

为了演示角色,我们将为之前创建的每个组添加一个管理员页面和一个会员页面。

更新 HomeController.cs

为新视图添加 2 个操作。

  [PrincipalPermission(SecurityAction.Demand, Role = "Admin")]
        public ActionResult Admin()
        {
            ViewBag.Message = "Your admin page.";

            return View();
        }

        [PrincipalPermission(SecurityAction.Demand, Role="Member")]
        public ActionResult Member()
        {
            ViewBag.Message = "Your member page.";

            return View();
        }
更新共享 _Layout.cshtml

向共享布局文件添加 2 个菜单链接到新页面。找到带有顶部菜单操作链接的导航栏,然后更改 <ul> 列表为:

<ul class="nav navbar-nav">
                    <li>@Html.ActionLink("Home", "Index", "Home")</li>
                    <li>@Html.ActionLink("About", "About", "Home")</li>
                    <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
                    @if (User.IsInRole("Member"))
                    {
                        <li>@Html.ActionLink("Member", "Member", "Home")</li>
                    }
                    @if (User.IsInRole("Admin"))
                    {
                        <li>@Html.ActionLink("Admin", "Admin", "Home")</li>
                    }
                    </ul> 
创建 Admin.cshtml 部分视图。
@{
    ViewBag.Title = "Admin";
}
<h2>@ViewBag.Title.</h2>
<h3>@ViewBag.Message</h3>

<p>Welcome to your admin page.</p> 
创建 Member.cshtml 部分视图。
@{
    ViewBag.Title = "Member";
}
<h2>@ViewBag.Title.</h2>
<h3>@ViewBag.Message</h3>

<p>Welcome to your member page.</p> 

现在一切就绪,当您运行应用程序并以管理员用户身份登录时,您应该会看到管理员菜单链接,并且能够导航到管理员页面。

Admin logged in

Admin Page

如果我们尝试在地址栏中导航到会员页面,身份验证系统将不允许。

Admin Security Exception

如果我们以会员用户身份登录,我们可以看到管理员菜单已消失,取而代之的是用户菜单。

历史

2014 年 4 月 2 日 - 初始版本

© . All rights reserved.