IdentityServer4 中文文档 -15- (快速入门)添加 JavaScript 客户端


该快速入门将展示如何搭建一个 JavaScript 客户端应用程序,其中的用户将登陆到 IdentityServer,使用 IdentityServer 发布的访问令牌调用 Web API,然后从 IdentityServer 注销。

新的 JavaScript 客户端项目

创建一个新的 JavaScript 应用程序项目。这可以是一个简单的空的 Web 项目,或者空的 ASP.NET Core 应用程序。这里将使用空的 ASP.NET Core 应用程序。

创建一个新的 ASP.NET Core Web 应用程序:

15、添加 JavaScript 客户端 - 图1

选择 “空” 模板:

15、添加 JavaScript 客户端 - 图2

点击“确定”按钮以创建项目。

修改宿主

修改宿主(像之前描述的一样)以让应用程序运行在 5003 端口上。

添加静态文件中间件

考虑到该项目主要运行于客户端,我们需要 ASP.NET 提供构成应用程序的静态 HTML 和 JavaScript 文件。静态文件中间件被设计来做这些事。 添加 NuGet 程序包 Microsoft.AspNetCore.StaticFiles

15、添加 JavaScript 客户端 - 图3

注册静态文件中间件

接下来是在 Startup.cs 文件的 Configure 方法中注册静态文件中间件:

  1. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
  2. {
  3. app.UseDefaultFiles();
  4. app.UseStaticFiles();
  5. }

中间件现在将能够从应用程序的 ~/wwwroot 目录提供静态文件 —— 这是我们将要放置 Html 和 JavaScript 文件的地方。

引用 oidc-client

在 MVC 项目中,我们使用了一个代码库来处理 OpenID Connect 协议。在该项目中我们也需要一个相似的库,只是这个库要在 JavaScript 中工作并且设计运行于浏览器端。oidc-client 库就是这样一个库,可以通过 NPMBower 等获取到该库,还可以直接从 github 下载该库。

NPM

如果你想通过 NPM 来下载 oidc-client ,可以按照这些步骤来做:

向你的项目中添加一个新的 NPM 程序包定义文件并命名为 package.json

15、添加 JavaScript 客户端 - 图4

package.json 文件的 devDependency 结点中添加一个 oidc-client 引用:

  1. "devDependencies": {
  2. "oidc-client": "1.3.0"
  3. }

一旦你保存该文件,Visual Studio 应该会自动还原这些程序包到一个名为 node_modules 的目录下:

15、添加 JavaScript 客户端 - 图5

~/node_modules/oidc-client/dist 目录下找到名为 oidc-client.js 的文件,并将其复制到应用程序的 ~/wwwroot 目录。有其他更为复杂的方式可以将你的 NPM 程序包复制到 ~/wwwroot 目录下,但是这些技术超出了当前快速入门的范围。

添加你的 Html 和 JavaScript 文件

接下来是将你的 HTML 和 JavaScript 文件添加到 ~/wwwroot 目录下。这里涉及到两份 HTML 文件和一份面向具体应用程序的 JavaScript 文件(不同于 oidc-client.js 库的文件)。在 ~/wwwroot 目录下,添加一份名为 index.html 和一份名为 callback.html 的 HTML 文件,还有一份名为 app.js 的 JavaScript 文件。

index.html

这将是你应用程序的主页面,它将简单地包含用于用户登录、注销和调用 Web API 的按钮,还将包含引用上述两份 JavaScript 文件的 <script> 标签和用于向用户显示消息的 <pre> 标签。

它看起来应该是这样的:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8" />
  5. <title></title>
  6. </head>
  7. <body>
  8. <button id="login">Login</button>
  9. <button id="api">Call API</button>
  10. <button id="logout">Logout</button>
  11. <pre id="results"></pre>
  12. <script src="oidc-client.js"></script>
  13. <script src="app.js"></script>
  14. </body>
  15. </html>

app.js

该文件将包含我们应用程序的主要代码。首先我们要在里面添加一个帮助器函数来将消息记录到 <pre> 标签:

  1. function log() {
  2. var results = document.getElementById('results');
  3. results.innerText = '';
  4. Array.prototype.forEach.call(arguments, function (msg) {
  5. if (msg instanceof Error) {
  6. msg = "错误:" + msg.message;
  7. } else if (typeof msg !== 'string') {
  8. msg = JSON.stringify(msg, null, 2);
  9. }
  10. results.innerHTML += msg + '\r\n';
  11. });
  12. }

然后添加代码,为上述三个按钮注册 “click” 事件处理程序:

  1. document.getElementById('login').addEventListener('click', login, false);
  2. document.getElementById('api').addEventListener('click', api, false);
  3. document.getElementById('logout').addEventListener('click', logout, false);

接着我们可以使用 oidc-client 库中的 UserManager 类型来管理 OpenID Connect 协议。它要求与 MVC 客户端中需要的相似的配置(虽然其中的值不尽相同)。添加这些代码以配置和初始化 UserManager

  1. var config = {
  2. authority: 'http://localhost:5000',
  3. client_id: 'js',
  4. redirect_uri: 'http://localhost:5003/callback.html',
  5. response_type: 'id_token token',
  6. scope: 'openid profile api1',
  7. post_logout_redirect_uri: 'http://localhost:5003/index.html'
  8. };
  9. var manager = new Oidc.UserManager(config);

UserManager 提供了一个 getUser API 来确定用户是否已经登录到 JavaScript 应用程序。其用了一个 JavaScript Promise 来返回异步结果。返回的 User 对象具有一个包含用户身份信息的 profile 属性。添加以下代码以检测用户是否已经登录到 JavaScript 应用程序:

  1. manager.getUser().then(function (user) {
  2. if (user) {
  3. log('用户已登录', user.profile);
  4. } else {
  5. log('用户未登录');
  6. }
  7. });

接下来我们要实现 loginapilogout 等方法。UserManager 提供了 signinRedirect 来登录用户,还提供了 signoutRedirect 来注销用户。在上述代码中我们获取到的 User 对象还有一个 access_token 属性,这可以用来认证到 web API。access_token 将通过 Bearer 模式的 Authorization header 被传递到 web API。添加以下代码以实现我们应用程序中的这三个功能:

  1. function login() {
  2. manager.signinRedirect();
  3. }
  4. function api() {
  5. manager.getUser().then(function (user) {
  6. var url = "http://localhost:5001/identity";
  7. var xhr = new XMLHttpRequest();
  8. xhr.open("GET", url);
  9. xhr.onload = function () {
  10. log(xhr.status, JSON.parse(xhr.responseText));
  11. }
  12. xhr.setRequestHeader("Authorization", "Bearer " + user.access_token);
  13. xhr.send();
  14. });
  15. }
  16. function logout() {
  17. manager.signoutRedirect();
  18. }

callback.html

当用户登录到 IdentityServer 后,该 HTML 文件是指定的 redirect_uri 页面,它将完成与 IdentityServer 间 OpenID Connect 协议的登录握手。之前我们使用的 UserManager 提供了所有实现这些的代码。一旦登录完成,我们就可以将用户重定向回 index.html 主页面。添加以下代码以完成登录过程:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8" />
  5. <title>登录回调</title>
  6. </head>
  7. <body>
  8. <script src="scripts/oidc-client.js"></script>
  9. <script>
  10. new Oidc.UserManager().signinRedirectCallback().then(function () {
  11. window.location = 'index.html';
  12. }).catch(function (e) {
  13. console.error(e);
  14. });
  15. </script>
  16. </body>
  17. </html>

添加 JavaScript 客户端定义

现在客户端应用程序已经准备好运行了,我们需要在 IdentityServer 中为这个新的 JavaScript 客户端定义一个配置入口。在 IdentityServer 项目中定位到客户端配置(在 Config.cs 文件中),然后为我们新的 JavaScript 应用程序向列表中添加新的 Client 定义。配置看起来应该是这样的:

  1. new Client
  2. {
  3. ClientId="js",
  4. ClientName="JavaScript 客户端",
  5. AllowedGrantTypes = GrantTypes.Implicit,
  6. AllowAccessTokensViaBrowser=true,
  7. RedirectUris = { "http://localhost:5003/callback.html" },
  8. PostLogoutRedirectUris = { "http://localhost:5003/index.html" },
  9. AllowedCorsOrigins = { "http://localhost:5003" },
  10. AllowedScopes =
  11. {
  12. IdentityServerConstants.StandardScopes.OpenId,
  13. IdentityServerConstants.StandardScopes.Profile,
  14. "api1"
  15. }
  16. }

允许 Ajax 以 CORS 的方式调用 Web API

最后需要配置的是 web API 项目中的 CORS。这将允许 Ajax 调用从 http://localhost:5003 跨越到 http://localhost:5001

CORS NuGet 程序包

添加 Microsoft.AspNetCore.Cors NuGet 程序包。

15、添加 JavaScript 客户端 - 图6

配置 CORS

接着在 Startup.cs 文件的ConfigureServices 方法中将 CORS 服务添加到依赖注入系统:

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. services.AddCors(options =>
  4. {
  5. // 这里定义一个 CORS 名为“default”的代理。
  6. options.AddPolicy("default", policy =>
  7. {
  8. policy.WithOrigins("http://localhost:5003")
  9. .AllowAnyHeader()
  10. .AllowAnyMethod();
  11. });
  12. });
  13. services.AddMvcCore()
  14. .AddAuthorization()
  15. .AddJsonFormatters();
  16. }

最后,在 Configure 方法中将 CORS 中间件添加到管道:

  1. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
  2. {
  3. loggerFactory.AddConsole(Configuration.GetSection("Logging"));
  4. loggerFactory.AddDebug();
  5. app.UseCors("default");
  6. app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions()
  7. {
  8. Authority = "http://localhost:5000",
  9. RequireHttpsMetadata = false,
  10. ApiName = "api1"
  11. });
  12. app.UseMvc();
  13. }

运行 JavaScript 客户端

现在你应该能够运行 JavaScript 客户端了:

15、添加 JavaScript 客户端 - 图7

点击 “登录” 按钮以登录用户。当用户被返回到 JavaScript 应用程序时,你应该能够看到他们的身份信息:

15、添加 JavaScript 客户端 - 图8

然后点击 “调用 API” 以调用 web API:

15、添加 JavaScript 客户端 - 图9

最后点击 “注销” 以注销用户。

你现在已经入门了使用 IdentityServer 进行登录,注销以及认证 Web API 调用的 JavaScript 客户端应用程序。