在企业级应用程序中,基于角色的认证(Role-Based Access Control, RBAC)是一种常见的安全机制。它允许系统管理员根据用户的角色分配不同的访问权限。例如,信用控制用户可以看到信用历史,而订单处理用户只需要知道账户有信用并且可以下单。本文将介绍如何使用Azure Active Directory(AAD)来实现基于角色的认证,并将其集成到ASP.NET应用程序中。
在ASP.NET中,实现基于角色的认证通常有以下几种方法:
IPrincipal.IsInRole(role)
方法。[PrincipalPermission(user, role)]
属性。PrincipalPermission(user, role)
实例并调用Demand()
方法。这些方法通常与来自活动目录或数据库的组一起使用,通过ASP.NET角色提供程序来实现。更多信息可以查看。
如果想要使用Azure Active Directory并使用活动目录组来启用基于角色的认证,这不是原生支持的,需要查询"Azure Active Directory Graph API"。有关Azure Active Directory及其优势的更多信息,可以查看。
接下来,将讨论如何使用NuGet包Azure.ActiveDirectory
来轻松地为应用程序添加角色认证。
默认情况下,Visual Studio项目向导会设置项目使用Entity Framework作为ValidatingIssuerNameRegistry
。由于在Azure中SQL存储成本较高,将切换到价格低廉的表存储。随着时间的推移,希望能够改进NuGet体验,减少手动步骤。本文的其余部分假设有一个Azure帐户和一个活动目录,或者可以从管理门户轻松创建一个新的。
在管理门户中,创建了2个用户和2个组在目录codeproject.onmicrosoft.com
中:
admin@codeproject.onmicrosoft.com
作为"Admin"组的成员,并具有"Global Administrator Role",这将允许在运行Visual Studio向导时在活动目录中创建应用程序。member@codeproject.onmicrosoft.com
作为"Member"组的成员。将使用这些用户来测试角色认证是否在MVC示例应用程序中工作。
创建一个新的ASP.NET项目,选择MVC项目模板,并选择"更改认证"按钮,配置MVC项目使用活动目录。由于希望启用Azure图API返回组,需要确保选择了允许读取目录数据的选项。
已经输入了活动目录名称codeproject.onmicrosoft.com
,并选择了"Single Sign On, Read directory data"以允许访问图。
App ID URI用于在Azure目录中标识应用程序,默认情况下这是从域名和项目名称自动生成的。
当点击OK时,使用管理员用户登录。
一旦Visual Studio创建了项目,需要在包管理器控制台中运行以下命令:
Install-Package Azure.ActiveDirectory
Install-Package Microsoft.WindowsAzure.ConfigurationManager
(出于某种原因,NuGet依赖项没有被添加)Update-Package
(可选更新到最新版本)现在需要对web.config
进行一些修改,以注册存储帐户和AzureTableIssuerNameRegistry
。
AzureTableIssuerNameRegistry
实现了ValidatingIssuerNameRegistry
在Azure表存储上,而不是默认注册的实体框架提供程序。
默认情况下,配置将指向应用程序中由向导创建的DatabaseIssuerNameRegistry
。
更新issuerNameRegistry
指向包注册表:
<issuerNameRegistry type="Azure.ActiveDirectory.AzureTableIssuerNameRegistry, Azure.ActiveDirectory" />
为了使AzureTableIssuerNameRegistry
工作,需要设置appSettings或CloudSettings配置键Identity.StorageConnectionString为存储连接字符串,应该类似于:
<add key="Identity.StorageConnectionString" value="DefaultEndpointsProtocol=https;AccountName=[name];AccountKey=[key]" />
正确的连接字符串可以在Azure管理门户中轻松找到,或者在Visual Studio的服务器资源管理器属性面板中。
在App_Start文件夹下创建的IdentityConfig
需要更新,以从Azure刷新其密钥,而不是默认的EF注册表。更新方法RefreshValidationSettings()
:
public static void RefreshValidationSettings()
{
string metadataLocation = ConfigurationManager.AppSettings["ida:FederationMetadataLocation"];
Azure.ActiveDirectory.AzureTableIssuerNameRegistry.RefreshKeys(metadataLocation);
}
在ConfigureIdentity()
方法中,需要告诉身份配置从Azure图中获取角色声明。通过添加以下行来实现这一点:
FederatedAuthentication.FederationConfiguration.IdentityConfiguration.ClaimsAuthenticationManager = new Azure.ActiveDirectory.GraphClaimsAuthenticationManager();
现在可以按F5运行应用程序,当提示登录时,应该能够登录并呈现默认站点。
默认情况下,HomeController
声明了一堆变量,并请求图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();
}
}
}
由于不再使用ASP.NET项目中创建的UserProfile
,需要快速更新视图以使用对象。
@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>
为了演示角色,将为之前创建的每个组添加一个管理员页面和一个成员页面。
添加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();
}
在共享布局文件中为新页面添加2个菜单链接,找到顶部菜单操作链接的navbar,并更改
<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>
@{
ViewBag.Title = "Admin";
}
<h2>@ViewBag.Title.
</h2>
<h3>@ViewBag.Message
</h3>
<p>Welcome to your admin page.
</p>
@{
ViewBag.Title = "Member";
}
<h2>@ViewBag.Title.
</h2>
<h3>@ViewBag.Message
</h3>
<p>Welcome to your member page.
</p>