无责任Windows Azure SDK .NET开发入门(二):使用Azure AD 进行身份验证
《編者按》本篇为系列文章,带领读者轻松进入Windows Azure SDK .NET开发平台。本文为第二篇,将教导读者使用Azure AD进行身分验证。也推荐读者阅读无责任Windows Azure SDK .NET开发入门(一):开发前准备工作。
使用Azure AD 进行身份验证
之所以将Azure AD 作为开始,是应为基本上我们所有应用都需要进行安全管理。Azure Active Directory (Azure AD) 通过以下方式简化了对开发人员的身份验证:将标识提供为一项服务、支持行业标准协议(例如 OAuth 2.0 和 OpenID Connect),并提供用于不同平台的开源库来帮助你快速开始编码。
Azure Active Directory (Azure AD) 提供了一种简便的方法,让企业可以在云中和本地管理标识和访问。你的用户可以使用相同的工作或学校帐户,以单一登录到任何云和本地 Web 应用程序。你的用户可以使用其最喜欢的设备,包括 iOS、Mac OS X、Android 和 Windows。你的组织可以使用集成的多因素身份验证,在本地和云中保护敏感数据和应用程序,从而确保本地和远程访问的安全。
而且Azure Active Directory (AD) 在大多数 Microsoft 云服务后面提供核心目录和身份管理功能。这些服务包括但不限于:
- Azure
- Microsoft Office 365
- Microsoft Dynamics CRM Online
- Windows Intune
一、使用Azure AD需要了解几个概念
(一)Azure AD目录
当你注册 Microsoft 云服务时,便会获得一个 Azure AD 目录。你可根据需要创建更多的目录。例如,可以将第一个目录保留为生产目录,然后创建另一个目录进行测试或过渡。
(二)Azure AD租户
在启用云的工作区中,可以将租户定义为拥有并管理该云服务的特定实例的客户端或组织。租户使用 Microsoft Azure 提供的标识平台,它只是你的组织在注册 Azure 或 Office 365 等 Microsoft 云服务时接收并拥有的 Azure Active Directory (Azure AD) 专用实例。每个 Azure AD 目录都是独特的,独立于其他 Azure AD 目录。就像公司办公大楼是你的组织特有的安全资产一样,根据设计,Azure AD 目录也是仅供你的组织使用的安全资产。Azure AD 体系结构隔离了客户数据和身份信息,避免混合存放。这意味着,一个 Azure AD 目录的用户和管理员不可能意外或恶意性地访问另一目录中的数据。
上图说明我现在有两个Azure AD目录,当然也就是有两个Azure AD租户。
现在,当你注册 Azure 时,将自动创建一个目录,你的订阅将与该目录相关联。使用 Azure AD 不收取费用。目录是免费资源。还有一个收费的Azure Active Directory 高级版级别,它提供额外的功能,例如公司品牌和自助密码重置。有意思的是Azure AD目录建立完毕后是可以改名字的,所以你在初次起名字时就不必犹犹豫豫了。
二、身份验证开发
在我们的案例中,我们是用户通过Web应用程序进行身份识别。
上面的图示说明了如下的一些概念
- Azure AD 是标识提供程序,负责对组织的目录中存在的用户和应用程序的标识进行验证,并最终在那些用户和应用程序成功通过身份验证时颁发安全令牌。
- 希望将身份验证外包给 Azure AD 的应用程序必须在 Azure AD 中进行注册,Azure AD 将在目录中注册并唯一地标识该应用程序。
- 在用户通过身份验证后,应用程序必须对用户的安全令牌进行验证以确保身份验证对于目标方是成功的。
- 身份验证过程的请求和响应流是由所使用的身份验证协议(例如 OAuth 2.0、OpenID Connect、WS-Federation 或 SAML 2.0)决定的
这章我们将简单的对Azure AD的开发进行入门性的尝试,包括身份验证登录、基本身份信息的存储。要完成Azure AD的应用开发需要在Azure门户和开发环境中各自完成一些工作。
Azure AD开发部分
首先我们需要引入以下组件库
- Microsoft.IdentityModel.Clients.ActiveDirectory
- Microsoft.IdentityModel.Protocol.Extensions
- Microsoft.Owin
- Microsoft.Owin.Host.SystemWeb
- Microsoft.Owin.Security
- Microsoft.Owin.Security.Cookies
- Microsoft.Owin.Security.OpenIdConnect
- Microsoft.Web.Infrastructure
- Microsoft.WindowsAzure.ConfigurationManager.
- System.IdentityModel.Tokens.Jwt
其次我们需要在Web.config文件的appSettings节添加如下配置节点
<add key="ida:ClientId" value=" " />
<add key="ida:Tenant" value=" " />
<add key="ida:TenantId" value="" />
<add key="ida:AADInstance" value="https://login.chinacloudapi.cn/{0}" />
<add key="ida:PostLogoutRedirectUri" value=" " />
<add key="ida:GraphUrl" value="https://graph.chinacloudapi.cn" />
<add key="ida:AppKey" value="" />
注意,由于我们使用的是世纪互联的版本,所以ida:AADInstance、ida:GraphUrl的Url将固定为https://login.chinacloudapi.cn/{0}和https://graph.chinacloudapi.cn。关于其他的配置值将在Azure AD门户部分讲解。
然后我们需要编写AuthenticationHelper类来辅助我们处理有关授权的基本处理,AuthenticationHelper有两大部分,第一部分是通过静态字段来读取在Web.config文件的appSettings节配置的有关Azure AD的信息。
public static readonly string Tenant = CloudConfigurationManager.GetSetting("ida:Tenant");
public static readonly string TenantId = CloudConfigurationManager.GetSetting("ida:TenantId");
public static readonly string LoginUrl = CloudConfigurationManager.GetSetting("ida:AADInstance");
public static readonly string GraphUrl = CloudConfigurationManager.GetSetting("ida:GraphUrl");
public static readonly string AppKey = CloudConfigurationManager.GetSetting("ida:AppKey");
public static readonly string AuthorityUrl = String.Format(CultureInfo.InvariantCulture, LoginUrl, TenantId);
public static readonly string AuthString = CloudConfigurationManager.GetSetting("ida:Auth") + CloudConfigurationManager.GetSetting("ida:Tenant");
public static readonly string ClaimsSchemas = "http://schemas.microsoft.com/identity/claims/objectidentifier";
public static readonly string ClientId = CloudConfigurationManager.GetSetting("ida:ClientId");
public static readonly string ClientSecret = CloudConfigurationManager.GetSetting("ida:ClientSecret");
public static readonly string PostLogoutRedirectUri = CloudConfigurationManager.GetSetting("ida:PostLogoutRedirectUri");
你已经注意到了,上面的代码在读取配置的时候没有使用我们传统的WebConfigurationManager类,而是采用了Azure新加入的CloudConfigurationManager类,该类是用于访问 Microsoft Azure 配置设置的静态类,可以从运行应用程序的平台的相应配置存储中读取配置设置。Microsoft Azure 外部环境中运行的 .NET 应用程序通常在 web.config 或 app.config 文件中存储配置设置。不管在哪个环境中运行代码,都可以使用CloudConfigurationManager类从适当的配置文件中读取设置。
然后在AuthenticationHelper中添加一个方法AcquireTokenAsync,这个方法用于返回当前的用户凭证,如果没有凭证则抛出异常。
public static string Token;
public static async Task<string> AcquireTokenAsync()
{
if (Token == null || Token.IsEmpty())
{
throw new Exception("Authorization Required.");
}
return Token;
}
然后我们再添加GetActiveDirectoryClient方法来获取对Azure AD的客户端对象引用。
public static ActiveDirectoryClient GetActiveDirectoryClient()
{
Uri baseServiceUri = new Uri(GraphUrl);
ActiveDirectoryClient activeDirectoryClient = new ActiveDirectoryClient(new Uri(baseServiceUri, TenantId), async () => await AcquireTokenAsync());
return activeDirectoryClient;
}
AuthenticationHelper到此就完成了,我们建立一个控制器AzureActiveDirectoryController,这个控制器将包含以下方法来使用AuthenticationHelper类。
SignIn
SignOut
SignIn登录
代码非常简单,在当前上下文的采用OpenID进行身份询问获取
public void SignIn()
{
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext()
.Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
SignOut登出
同样代码简单明确vm
public void SignOut()
{
string userObjectID = ClaimsPrincipal.Current.FindFirst(AuthenticationHelper.ClaimsSchemas).Value;
var authContext = new AuthenticationContext(AuthenticationHelper.AuthorityUrl, new NaiveSessionCache(userObjectID));
authContext.TokenCache.Clear();
AuthenticationHelper.Token = null;
HttpContext.GetOwinContext().Authentication.SignOut(OpenIdConnectAuthenticationDefaults.AuthenticationType, CookieAuthenticationDefaults.AuthenticationType);
}
好了,现在登入登出的代码已经完成了,我们可以尝试下载我们的MVC Web APP中执行体验下。在MVC自己建立的架构中我们可以循着View\ Shared\ _LoginPartial.cshtml找到通用的登入登出的View界面,然后修改代码为
@if (Request.IsAuthenticated)
{
<text>
<ul class="nav navbar-nav navbar-right">
<li class="navbar-text">
Hello, @User.Identity.Name!
</li>
<li>
@Html.ActionLink("Sign out", "SignOut", "AzureActiveDirectory")
</li>
</ul>
</text>
}
else
{
<ul class="nav navbar-nav navbar-right">
<li>@Html.ActionLink("Sign in", "SignIn", "AzureActiveDirectory", routeValues: null, htmlAttributes: new { id = "loginLink" })</li>
</ul>
}
上面的代码通过一个if完成登陆后出现Sign out链接和登出状态为Sign in链接。然后在View\ Shared\ _LoginPartial.cshtml加入这个_LoginPartial.cshtml我们就在主模板拥有了登录登出的链接功能。我对_LoginPartial.cshtml此处的修改如下
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>@Html.ActionLink("主页", "Index", "Home")</li>
<li>@Html.ActionLink("关于", "About", "Home")</li>
<li>@Html.ActionLink("联系方式", "Contact", "Home")</li>
</ul>
@Html.Partial("_LoginPartial")
</div>
现在我们执行代码后,我们看到的默认页面是
点击Sign in链接后将跳转到微软的登录界面
点击账号后
输入正确的密码后,将跳回我们应用的默认首页,并且明显的发现我们已经使用了正确的身份登入了。
(责任编辑 / 张之颖)