通过使用 Azure Active Directory (Azure AD),可以支持 ASP.NET MVC Web 应用程序的单一登录 (SSO) 或 Web API 服务中的 Active Directory 身份验证。 通过 Azure AD 身份验证,用户可以使用其帐户从 Azure Active Directory 连接到 Web 应用程序。 使用 Web API 进行 Azure AD 身份验证的优点包括从 Web 应用程序公开 API 时提供增强的数据安全性。 通过 Azure AD,不需要使用其自己的帐户和用户管理来管理单独的身份验证系统。

本文及其同类文章提供了对 Active Directory 使用 Visual Studio 连接服务功能的详细信息。 Visual Studio 2015 及更高版本提供了该功能。

目前,Active Directory 连接服务不支持 ASP.NET Core 应用程序。

先决条件

  • Azure 帐户:如果没有 Azure 帐户,可以注册免费试用帐户,或者激活 Visual Studio 订户权益。
  • Visual Studio 2015 或更高版本。 立即下载 Visual Studio。

使用“连接服务”对话框连接到 Azure Active Directory

  1. 在 Visual Studio 中,创建或打开 ASP.NET MVC 项目或 ASP.NET Web API 项目。 可以使用 MVC、Web API、单页应用程序、Azure API 应用、Azure 移动应用和 Azure 移动服务模板。
  2. 选择“项目”>“添加连接服务...” 菜单命令,或双击解决方案资源管理器中项目下的“连接的服务” 节点。
  3. 在“连接服务” 页,选择“使用 Azure Active Directory 进行身份验证” 。
  4. 在“简介” 页上,选择“下一步” 。 如果在此页上看到错误,请参阅使用 Azure Active Directory 连接服务诊断错误。
  5. 在“单一登录” 页上,从“域” 下拉列表中选择域。 该列表包含在 Visual Studio 的“帐户设置”对话框(“文件”>“帐户设置...” )中列出的帐户可以访问的所有域。如果没有找到要查找的域,作为替代方法,可以输入域名,如 mydomain.onmicrosoft.com。 可以选择用于创建 Azure Active Directory 应用的选项,也可以使用现有 Azure Active Directory 应用中的设置。 完成后,选择“下一步” 。
  6. 在“目录访问权限” 页上,根据需要选择“读取目录数据” 。 开发人员通常会选择此选项。
  7. 选择“完成” 以开始对项目进行修改,从而启用 Azure AD 身份验证。 Visual Studio 在此期间会显示进度:
  8. 处理完成后,Visual Studio 会根据项目类型,在浏览器中打开以下文章之一:
  • .NET MVC 项目入门
  • WebAPI 项目入门
  1. 还可以在 Azure 门户中看到 Active Directory 域。

 MVC项目大致修改

 1,在config.json文件中添加AzureAD的配置信息:

<add key="ida:ClientId" value="a3fe-da85408871cf" />
<add key="ida:Tenant" value="....onmicrosoft.com" />
<add key="ida:TenantId" value="-375c77a8f936" />
<add key="ida:Audience" value="https://localhost:44312/" />
<add key="ida:Password" value="~oTuY01i39" />
<add key="ida:MetadataAddress" value="https://login.microsoftonline.com/" />

注意:如果本身系统有Form登录验证,配置如下:

<authentication mode="form">
    <forms loginUrl="~/Login" />
</authentication>

那必须将Mode方式改为none,否则授权失败,不会跳转到Azure AD登录授权页面,而是定向到系统默认的Login页面:

<authentication mode="None"></authentication>

2,在Startup.Auth.cs中修改如下:

// 有关配置身份验证的详细信息,请访问 https://go.microsoft.com/fwlink/?LinkId=868025
        public void ConfigureAuth(IAppBuilder app)
        {            
            app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
            app.UseCookieAuthentication(new CookieAuthenticationOptions());

            app.UseOpenIdConnectAuthentication(
            new OpenIdConnectAuthenticationOptions
            {
                SaveTokens = true,
                RedeemCode = true,
                ClientId = AuthorizeADHelper.ClientId,
                Authority = AuthorizeADHelper.Authority,
                RedirectUri = AuthorizeADHelper.PostLogoutRedirectUri,
                PostLogoutRedirectUri = AuthorizeADHelper.PostLogoutRedirectUri,

                Notifications = new OpenIdConnectAuthenticationNotifications()
                {
                    // 如果 OpenID Connect 响应中存在代码,请将其兑换成访问令牌,并刷新令牌,然后进行存储。
                    AuthorizationCodeReceived = OnAuthorizationCodeReceived,
                    AuthenticationFailed = OnAuthenticationFailed

                }
            });
        }
        private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
        {
            var code = context.Code;
            ClientCredential credential = new ClientCredential(AuthorizeADHelper.ClientId, AuthorizeADHelper.AppKey);

            string userObjectID = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
            string tenantID = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;

            AuthenticationContext authContext = new AuthenticationContext(AuthorizeADHelper.Authority, new ADALTokenCache(userObjectID));

            // If you create the redirectUri this way, it will contain a trailing slash.  
            // Make sure you've registered the same exact Uri in the Azure Portal (including the slash).
            Uri uri = new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path));

            AuthenticationResult result11 = await authContext.AcquireTokenByAuthorizationCodeAsync(code, uri, credential, AuthorizeADHelper.GraphResourceId);
            AuthenticationResult authenticationResult = await authContext.AcquireTokenSilentAsync(AuthorizeADHelper.GraphResourceId, credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));

            Uri servicePointUri = new Uri(AuthorizeADHelper.GraphResourceId);
            Uri serviceRoot = new Uri(servicePointUri, tenantID);
            var activeDirectoryClient = new ActiveDirectoryClient(serviceRoot, async () => await Task.FromResult(authenticationResult.AccessToken));
            // 使用该令牌查询图形以获取用户详细信息
            var result = await activeDirectoryClient.Users
                .Where(u => u.ObjectId.Equals(userObjectID))
                .ExecuteAsync();

            var user = result.CurrentPage.ToList().First();

            if (user != null)
            {
                //本身的登陆机制代码
            }

        }
        /// <summary>
        /// Handle failed authentication requests by redirecting the user to the home page with an error in the query string
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
        {
            context.HandleResponse();
            context.Response.Redirect("/?errormessage=" + context.Exception.Message);
            return Task.FromResult(0);
        }

3,添加一个名为AzureADController的Controller:

// GET: /AzureAD/Login
  [HttpGet]
  public IActionResult Login()
  {
    if (Context.User == null || !Context.User.Identity.IsAuthenticated)
      return new ChallengeResult(OpenIdConnectAuthenticationDefaults.AuthenticationScheme, new AuthenticationProperties { RedirectUri = "/" });
    return RedirectToAction("Index", "Home");
  }
 
  // GET: /AzureAD/LogOff
  [HttpGet]
  public IActionResult LogOff()
  {
    if (Context.User.Identity.IsAuthenticated)
    {
      Context.Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationScheme);
      Context.Authentication.SignOut(OpenIdConnectAuthenticationDefaults.AuthenticationScheme);
    }
    return RedirectToAction("Index", "Home");
  }

如果你遇到添加了 [Authorize] ,但是不能自动转到登录页面的情况:看上面加红的文字(或者加上下面代码)

app.UseOpenIdConnectAuthentication(options => {
  options.AutomaticAuthentication = true;
});