使用 ASP.NET Web API (C#) 的外部身份验证服务

Visual Studio 2017 和 ASP.NET 4.7.2 扩展 了单页应用程序 (SPA) 和 Web API 服务的安全选项,以与外部身份验证服务集成,其中包括多个 OAuth/OpenID 和社交媒体身份验证服务:Microsoft 帐户、Twitter、Facebook 和 Google。

在本演练中

先决条件

若要按照本演练中的示例操作,需要具备以下各项:

使用外部身份验证服务

Web 开发人员目前可用的大量外部身份验证服务有助于在创建新的 Web 应用程序时缩短开发时间。 Web 用户通常为常用 Web 服务和社交媒体网站拥有多个现有帐户,因此,当 Web 应用程序从外部 Web 服务或社交媒体网站实现身份验证服务时,它节省了创建身份验证实现所花费的开发时间。 使用外部身份验证服务可避免最终用户为 Web 应用程序创建另一个帐户,也不必记住其他用户名和密码。

过去,开发人员有两种选择:创建自己的身份验证实现,或了解如何将外部身份验证服务集成到其应用程序中。 在最基本的级别,下图演示了用户代理 (Web 浏览器) 的简单请求流,该请求流从配置为使用外部身份验证服务的 Web 应用程序请求信息:

用户代理的简单请求流的插图

在上图中,此示例中的用户代理 (或 Web 浏览器) 向 Web 应用程序发出请求,该应用程序会将 Web 浏览器重定向到外部身份验证服务。 用户代理将其凭据发送到外部身份验证服务,如果用户代理已成功进行身份验证,外部身份验证服务将使用某种形式的令牌将用户代理重定向到原始 Web 应用程序,用户代理会将其发送到 Web 应用程序。 Web 应用程序将使用令牌来验证用户代理是否已由外部身份验证服务成功进行身份验证,并且 Web 应用程序可以使用令牌收集有关用户代理的详细信息。 应用程序处理完用户代理的信息后,Web 应用程序将根据其授权设置向用户代理返回相应的响应。

在此第二个示例中,用户代理与 Web 应用程序和外部授权服务器协商,Web 应用程序与外部授权服务器执行其他通信以检索有关用户代理的其他信息:

用户代理与 Web 应用程序协商的插图

Visual Studio 2017 和 ASP.NET 4.7.2 通过为以下身份验证服务提供内置集成,使开发人员更轻松地与外部身份验证服务的集成:

  • Facebook
  • Google
  • Microsoft 帐户 (Windows Live ID 帐户)
  • Twitter

本演练中的示例将演示如何使用 Visual Studio 2017 附带的新 ASP.NET Web 应用程序模板配置每个受支持的外部身份验证服务。

注意

如有必要,可能需要将 FQDN 添加到外部身份验证服务的设置中。 此要求基于某些外部身份验证服务的安全约束,这些服务要求应用程序设置中的 FQDN 与客户端使用的 FQDN 相匹配。 (对于每个外部身份验证服务,执行此操作的步骤差异很大;需要查阅每个外部身份验证服务的文档,了解这是否是必需的以及如何配置这些设置。) 如果需要配置IIS Express以使用 FQDN 来测试此环境,请参阅本演练后面的配置IIS Express以使用完全限定的域名部分。

创建示例 Web 应用程序

以下步骤将引导你使用 ASP.NET Web 应用程序模板创建一个示例应用程序,稍后在本演练中将此示例应用程序用于每个外部身份验证服务。

启动 Visual Studio 2017,然后从“开始”页中选择“ 新建项目 ”。 或者,从“ 文件 ”菜单中选择“ 新建 ”,然后选择“ 项目”。

显示“ 新建项目 ”对话框时,选择“ 已安装 ”,然后展开 “Visual C#”。 在 “Visual C#”下,选择“ Web”。 在项目模板列表中,选择 “ASP.NET Web 应用程序 (.Net Framework) ”。 输入项目的名称,然后单击“ 确定”。

“新建项目”对话框的图像

显示 “新建 ASP.NET 项目 ”时,选择“ 单页应用程序 ”模板并单击“ 创建项目”。

模板选择的图像

等待 Visual Studio 2017 创建项目。

Visual Studio 2017 完成项目创建后,打开位于 App_Start 文件夹中的 Startup.Auth.cs 文件。

首次创建项目时, 不会在 Startup.Auth.cs 文件中启用任何外部身份验证服务;下面说明了代码可能类似于的内容,其中突出显示了要启用外部身份验证服务和任何相关设置的部分,以便对 ASP.NET 应用程序使用 Microsoft 帐户、Twitter、Facebook 或 Google 身份验证:

using System;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.DataProtection;
using Microsoft.Owin.Security.Google;
using Microsoft.Owin.Security.OAuth;
using Owin;
using WebApplication1.Models;
using WebApplication1.Providers;

namespace WebApplication1
{
    public partial class Startup
    {
        // Enable the application to use OAuthAuthorization. You can then secure your Web APIs
        static Startup()
        {
            PublicClientId = "web";

            OAuthOptions = new OAuthAuthorizationServerOptions
            {
                TokenEndpointPath = new PathString("/Token"),
                AuthorizeEndpointPath = new PathString("/Account/Authorize"),
                Provider = new ApplicationOAuthProvider(PublicClientId),
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
                AllowInsecureHttp = true
            };
        }

        public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }

        public static string PublicClientId { get; private set; }

        // For more information on configuring authentication, please visit https://go.microsoft.com/fwlink/?LinkId=301864
        public void ConfigureAuth(IAppBuilder app)
        {
            // Configure the db context, user manager and signin manager to use a single instance per request
            app.CreatePerOwinContext(ApplicationDbContext.Create);
            app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
            app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

            // Enable the application to use a cookie to store information for the signed in user
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new PathString("/Account/Login"),
                Provider = new CookieAuthenticationProvider
                {
                    // Enables the application to validate the security stamp when the user logs in.
                    // This is a security feature which is used when you change a password or add an external login to your account.  
                    OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                        validateInterval: TimeSpan.FromMinutes(20),
                        regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
                }
            });
            // Use a cookie to temporarily store information about a user logging in with a third party login provider
            app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

            // Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
            app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));

            // Enables the application to remember the second login verification factor such as phone or email.
            // Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
            // This is similar to the RememberMe option when you log in.
            app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);

            // Enable the application to use bearer tokens to authenticate users
            app.UseOAuthBearerTokens(OAuthOptions);

            // Uncomment the following lines to enable logging in with third party login providers
            //app.UseMicrosoftAccountAuthentication(
            //    clientId: "",
            //    clientSecret: "");

            //app.UseTwitterAuthentication(
            //    consumerKey: "",
            //    consumerSecret: "");

            //app.UseFacebookAuthentication(
            //    appId: "",
            //    appSecret: "");

            //app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
            //{
            //    ClientId = "",
            //    ClientSecret = ""
            //});
        }
    }
}

按 F5 生成和调试 Web 应用程序时,将显示一个登录屏幕,你会看到未定义任何外部身份验证服务。

登录屏幕的图像

以下部分介绍如何在 Visual Studio 2017 中启用随 ASP.NET 提供的每个外部身份验证服务。

启用 Facebook 身份验证

使用 Facebook 身份验证需要创建 Facebook 开发人员帐户,并且项目需要 Facebook 的应用程序 ID 和密钥才能正常运行。 有关创建 Facebook 开发人员帐户和获取应用程序 ID 和密钥的信息,请参阅 https://go.microsoft.com/fwlink/?LinkID=252166

获取应用程序 ID 和密钥后,请使用以下步骤为 Web 应用程序启用 Facebook 身份验证:

  1. 在 Visual Studio 2017 中打开项目时,打开 Startup.Auth.cs 文件。

  2. 找到代码的 Facebook 身份验证部分:

    // Uncomment the following lines to enable logging in with third party login providers
    //app.UseMicrosoftAccountAuthentication(
    //    clientId: "",
    //    clientSecret: "");
    
    //app.UseTwitterAuthentication(
    //    consumerKey: "",
    //    consumerSecret: "");
    
    //app.UseFacebookAuthentication(
    //    appId: "",
    //    appSecret: "");
    
    //app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
    //{
    //    ClientId = "",
    //    ClientSecret = ""
    //});
    
  3. 删除“//”字符以取消注释突出显示的代码行,然后添加应用程序 ID 和密钥。 添加这些参数后,可以重新编译项目:

    // Uncomment the following lines to enable logging in with third party login providers
    //app.UseMicrosoftAccountAuthentication(
    //    clientId: "",
    //    clientSecret: "");
    
    //app.UseTwitterAuthentication(
    //   consumerKey: "",
    //   consumerSecret: "");
    
    //app.UseFacebookAuthentication(
    //   appId: "426f62526f636b73",
    //   appSecret: "");
    
    //app.UseGoogleAuthentication();
    
  4. 在 Web 浏览器中按 F5 打开 Web 应用程序时,会看到 Facebook 已定义为外部身份验证服务:

    已定义的 Facebook 图像

  5. 单击 Facebook 按钮时,浏览器将重定向到 Facebook 登录页:

    Facebook 登录页的图像

  6. 输入 Facebook 凭据并单击“ 登录”后,Web 浏览器将重定向回 Web 应用程序,这会提示输入要与 Facebook 帐户关联的 用户名

    Web 应用程序的图像

  7. 输入用户名并单击“ 注册 ”按钮后,Web 应用程序将显示 Facebook 帐户的默认 主页

    显示默认 Facebook 主页的图像

启用 Google 身份验证

使用 Google 身份验证需要创建 Google 开发人员帐户,项目需要 Google 提供的应用程序 ID 和密钥才能正常运行。 有关创建 Google 开发人员帐户和获取应用程序 ID 和密钥的信息,请参阅 https://developers.google.com

若要为 Web 应用程序启用 Google 身份验证,请使用以下步骤:

  1. 在 Visual Studio 2017 中打开项目时,打开 Startup.Auth.cs 文件。

  2. 找到代码的 Google 身份验证部分:

    // Uncomment the following lines to enable logging in with third party login providers
    //app.UseMicrosoftAccountAuthentication(
    //    clientId: "",
    //    clientSecret: "");
    
    //app.UseTwitterAuthentication(
    //    consumerKey: "",
    //    consumerSecret: "");
    
    //app.UseFacebookAuthentication(
    //    appId: "",
    //    appSecret: "");
    
    //app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
    //{
    //    ClientId = "",
    //    ClientSecret = ""
    //});
    
  3. 删除“//”字符以取消注释突出显示的代码行,然后添加应用程序 ID 和密钥。 添加这些参数后,可以重新编译项目:

    // Uncomment the following lines to enable logging in with third party login providers
    //app.UseMicrosoftAccountAuthentication(
    //    clientId: "",
    //    clientSecret: "");
    
    //app.UseTwitterAuthentication(
    //   consumerKey: "",
    //   consumerSecret: "");
    
    //app.UseFacebookAuthentication(
    //   appId: "",
    //   appSecret: "");
    
    app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
    {
        ClientId = "477522346600.apps.googleusercontent.com",
        ClientSecret = "gobkdpbocikdfbnfahjladnetpdkvmic"
    });
    
  4. 按 F5 在 Web 浏览器中打开 Web 应用程序时,会看到 Google 已定义为外部身份验证服务:

    已定义图像 Google

  5. 单击 Google 按钮时,浏览器将重定向到 Google 登录页:

    Google 登录页的图像

  6. 输入 Google 凭据并单击“ 登录”后,Google 将提示验证 Web 应用程序是否有权访问 Google 帐户:

    Google 权限的图像

  7. 单击“ 接受”时,Web 浏览器将重定向回 Web 应用程序,这会提示输入要与 Google 帐户关联的 用户名

    Web 应用程序提示要关联的用户名的图像

  8. 输入用户名并单击“ 注册 ”按钮后,Web 应用程序将显示 Google 帐户的默认 主页

    Google 默认主页的图像

启用 Microsoft 身份验证

Microsoft 身份验证要求你创建开发人员帐户,并且需要客户端 ID 和客户端密码才能正常运行。 有关创建 Microsoft 开发人员帐户和获取客户端 ID 和客户端密码的信息,请参阅 https://go.microsoft.com/fwlink/?LinkID=144070

获取使用者密钥和使用者机密后,请使用以下步骤为 Web 应用程序启用 Microsoft 身份验证:

  1. 在 Visual Studio 2017 中打开项目时,打开 Startup.Auth.cs 文件。

  2. 找到代码的 Microsoft 身份验证部分:

    // Uncomment the following lines to enable logging in with third party login providers
    //app.UseMicrosoftAccountAuthentication(
    //    clientId: "",
    //    clientSecret: "");
    
    //app.UseTwitterAuthentication(
    //   consumerKey: "",
    //   consumerSecret: "");
    
    //app.UseFacebookAuthentication(
    //   appId: "",
    //   appSecret: "");
    
    //app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
    //{
    //    ClientId = "",
    //    ClientSecret = ""
    //});
    
  3. 删除“//”字符以取消注释突出显示的代码行,然后添加客户端 ID 和客户端密码。 添加这些参数后,可以重新编译项目:

    // Uncomment the following lines to enable logging in with third party login providers
    app.UseMicrosoftAccountAuthentication(
        clientId: "426f62526f636b73",
        clientSecret: "57686f6120447564652c2049495320526f636b73");
    
    //app.UseTwitterAuthentication(
    //   consumerKey: "",
    //   consumerSecret: "");
    
    //app.UseFacebookAuthentication(
    //   appId: "",
    //   appSecret: "");
    
    //app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
    //{
    //    ClientId = "",
    //    ClientSecret = ""
    //});
    
  4. 按 F5 在 Web 浏览器中打开 Web 应用程序时,会看到 Microsoft 已定义为外部身份验证服务:

    Microsoft 定义的图像

  5. 单击 Microsoft 按钮时,浏览器将重定向到 Microsoft 登录页:

    Microsoft 登录页的图像

  6. 输入 Microsoft 凭据并单击“ 登录”后,系统会提示验证 Web 应用程序是否有权访问你的 Microsoft 帐户:

    Microsoft 权限的图像

  7. 单击“ ”时,Web 浏览器将被重定向回 Web 应用程序,这将提示你输入要与 Microsoft 帐户关联的 用户名

    关联的 Google 帐户的图像

  8. 输入用户名并单击“ 注册 ”按钮后,Web 应用程序将显示 Microsoft 帐户的默认 主页

    默认 Microsoft 主页的图像

启用 Twitter 身份验证

Twitter 身份验证要求你创建一个开发人员帐户,并且它需要使用者密钥和使用者机密才能正常运行。 有关创建 Twitter 开发人员帐户和获取使用者密钥和使用者机密的信息,请参阅 https://go.microsoft.com/fwlink/?LinkID=252166

获取使用者密钥和使用者机密后,请使用以下步骤为 Web 应用程序启用 Twitter 身份验证:

  1. 在 Visual Studio 2017 中打开项目时,打开 Startup.Auth.cs 文件。

  2. 找到代码的 Twitter 身份验证部分:

    // Uncomment the following lines to enable logging in with third party login providers
    //app.UseMicrosoftAccountAuthentication(
    //    clientId: "",
    //    clientSecret: "");
    
    //app.UseTwitterAuthentication(
    //   consumerKey: "",
    //   consumerSecret: "");
    
    //app.UseFacebookAuthentication(
    //   appId: "",
    //   appSecret: "");
    
    //app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
    //{
    //    ClientId = "",
    //    ClientSecret = ""
    //});
    
  3. 删除“//”字符以取消注释突出显示的代码行,然后添加使用者密钥和使用者机密。 添加这些参数后,可以重新编译项目:

    // Uncomment the following lines to enable logging in with third party login providers
    //app.UseMicrosoftAccountAuthentication(
    //    clientId: "",
    //    clientSecret: "");
    
    app.UseTwitterAuthentication(
       consumerKey: "426f62526f636b73",
       consumerSecret: "57686f6120447564652c2049495320526f636b73");
    
    //app.UseFacebookAuthentication(
    //   appId: "",
    //   appSecret: "");
    
    //app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
    //{
    //    ClientId = "",
    //    ClientSecret = ""
    //});
    
  4. 按 F5 在 Web 浏览器中打开 Web 应用程序时,会看到 Twitter 已定义为外部身份验证服务:

    Twitter 定义的图像

  5. 单击 Twitter 按钮时,浏览器将重定向到 Twitter 登录页面:

    Twitter 登录页的图像

  6. 输入 Twitter 凭据并单击“ 授权应用”后,Web 浏览器将重定向回 Web 应用程序,这将提示输入要与 Twitter 帐户关联的 用户名

    Web 应用中要关联的 Twitter 帐户的图像

  7. 输入用户名并单击“ 注册 ”按钮后,Web 应用程序将显示 Twitter 帐户的默认 主页

    默认 Twitter 主页的图像

其他信息

有关创建使用 OAuth 和 OpenID 的应用程序的其他信息,请参阅以下 URL:

组合外部身份验证服务

为了获得更大的灵活性,可以同时定义多个外部身份验证服务 - 这允许 Web 应用程序的用户使用任何已启用的外部身份验证服务的帐户:

多个外部身份验证服务的图像

将IIS Express配置为使用完全限定的域名

某些外部身份验证提供程序不支持使用 HTTP 地址(如 http://localhost:port/)测试应用程序。 若要解决此问题,可以将静态完全限定域名 (FQDN) 映射添加到 HOSTS 文件,并在 Visual Studio 2017 中配置项目选项以使用 FQDN 进行测试/调试。 为此,请按照以下步骤操作:

  • 添加静态 FQDN 映射您的 HOSTS 文件:

    1. 在 Windows 中打开提升的命令提示符。

    2. 输入以下命令:

      记事本 %WinDir%\system32\drivers\etc\hosts

    3. 将如下所示的条目添加到 HOSTS 文件:

      127.0.0.1 www.wingtiptoys.com

    4. 保存并关闭 HOSTS 文件。

  • 将 Visual Studio 项目配置为使用 FQDN:

    1. 在 Visual Studio 2017 中打开项目时,单击“ 项目 ”菜单,然后选择项目的属性。 例如,可以选择 “WebApplication1 属性”。
    2. 选择 Web 选项卡。
    3. 输入 项目 URL 的 FQDN。 例如,如果这是添加到 HOSTS 文件的 FQDN 映射,则输入 http://www.wingtiptoys.com
  • 将IIS Express配置为对应用程序使用 FQDN:

    1. 在 Windows 中打开提升的命令提示符。

    2. 键入以下命令以更改为IIS Express文件夹:

      cd /d “%ProgramFiles%\IIS Express”

    3. 键入以下命令将 FQDN 添加到应用程序:

      appcmd.exe设置 config -section:system.applicationHost/sites /+“[name='WebApplication1'].bindings。[protocol='http',bindingInformation='*:80:www.wingtiptoys.com']“ /commit:apphost

    其中 WebApplication1 是项目的名称, bindingInformation 包含要用于测试的端口号和 FQDN。

如何获取 Microsoft 身份验证的应用程序设置

将应用程序链接到 Windows Live for Microsoft 身份验证是一个简单的过程。 如果尚未将应用程序链接到 Windows Live,可以使用以下步骤:

  1. 在出现提示时浏览并 https://go.microsoft.com/fwlink/?LinkID=144070 输入 Microsoft 帐户名称和密码,然后单击“ 登录”:

  2. 选择“ 添加应用 ”,并在出现提示时输入应用程序的名称,然后单击“ 创建”:

    添加应用的图像

  3. “名称 ”下选择应用,随即显示其应用程序属性页。

  4. 输入应用程序的重定向域。 复制 应用程序 ID ,然后在“ 应用程序机密”下选择“ 生成密码”。 复制显示的密码。 应用程序 ID 和密码是客户端 ID 和客户端密码。 选择 “确定” ,然后选择“ 保存”。

    重定向域信息的图像

可选:禁用本地注册

当前 ASP.NET 本地注册功能不会阻止自动化程序 (机器人) 创建成员帐户;例如,通过使用机器人防护和验证技术(如 CAPTCHA)。 因此,应删除登录页上的本地登录表单和注册链接。 为此,请打开项目中的 _Login.cshtml 页面,然后注释掉本地登录面板和注册链接的行。 生成的页面应如以下代码示例所示:

<!-- ko with: login -->
<hgroup class="title">
    <h1>Log in</h1>
</hgroup>
<div class="row-fluid">
    @*<section class="span7">
        <form>
            <fieldset class="form-horizontal">
                <legend>Use a local account to log in.</legend>
                <ul class="text-error" data-bind="foreach: errors">
                    <li data-bind="text: $data"></li>
                </ul>
                <div class="control-group">
                    <label for="UserName" class="control-label">User name</label>
                    <div class="controls">
                        <input type="text" name="UserName" data-bind="value: userName, hasFocus: true" />
                        <span class="text-error" data-bind="visible: userName.hasError, text: userName.errorMessage"></span>
                    </div>
                </div>
                <div class="control-group">
                    <label for="Password" class="control-label">Password</label>
                    <div class="controls">
                        <input type="password" name="Password" data-bind="value: password" />
                        <span class="text-error" data-bind="visible: password.hasError, text: password.errorMessage"></span>
                    </div>
                </div>
                <div class="control-group">
                    <div class="controls">
                        <label class="checkbox">
                            <input type="checkbox" name="RememberMe" data-bind="checked: rememberMe" />
                            <label for="RememberMe">Remember me?</label>
                        </label>
                    </div>
                </div>
                <div class="form-actions no-color">
                    <button type="submit" class="btn" data-bind="click: login, disable: loggingIn">Log in</button>
                </div>
                <p><a href="#" data-bind="click: register">Register</a> if you don't have a local account.</p>
            </fieldset>
        </form>
    </section>*@
    <section class="span5">
        <h2>Log in using another service</h2>
        <div data-bind="visible: loadingExternalLogin">Loading...</div>
        <div data-bind="visible: !loadingExternalLogin()">
            <div class="message-info" data-bind="visible: !hasExternalLogin()">
                <p>
                    There are no external authentication services configured. See <a href="https://go.microsoft.com/fwlink/?LinkId=252166">this article</a>
                    for details on setting up this ASP.NET application to support logging in via external services.
                </p>
            </div>
            <form data-bind="visible: hasExternalLogin">
                <fieldset class="form-horizontal">
                    <legend>Use another service to log in.</legend>
                    <p data-bind="foreach: externalLoginProviders">
                        <button type="submit" class="btn" data-bind="text: name, attr: { title: 'Log in using your ' + name() + ' account' }, click: login"></button>
                    </p>
                </fieldset>
            </form>
        </div>
    </section>
</div>
<!-- /ko -->

禁用本地登录面板和注册链接后,登录页面将仅显示已启用的外部身份验证提供程序:

仅外部提供程序登录名的图像