一、设置和概述
二、使用客户端凭证保护API
三、使用密码保护API
四、使用OpenID Connect添加用户验证
五、添加对外部认证的支持
六、切换到Hybrid Flow并添加API访问权限
七、Using ASP.NET Core Identity
八、添加一个JavaScript客户端
九、使用EntityFramework core进行配置和操作数据
十、社区快速入门&样品
一、设置和概述
1,使用 dotnet new mvc 创建一个mvc项目
2,nuget IdentityServer4
3,修改Startup
public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddIdentityServer()//在DI中注册IdentityServer服务,它还为运行时状态注册一个内存存储 .AddDeveloperSigningCredential();//设置临时签名凭证 } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseIdentityServer(); } }
二、使用客户端凭证保护API
在这个场景中,我们将定义一个API和一个希望访问它的客户端。客户端将在IdentityServer中请求访问令牌,并使用它获得对API的访问。
定义API
创建一个Config,cs文件,在该文件中定义您希望保护的系统中的资源,例如api。
public static IEnumerableGetApiResources() { return new List { new ApiResource("api1", "My API") }; }
定义Client
定义访问这个api的client
对于此场景,客户端将没有交互式用户,并将使用所谓的client secret与IdentityServer进行身份验证。将以下代码添加到配置中。cs文件:
public static IEnumerableGetClient() { return new List () { new Client(){ ClientId="client", //没有交互式用户,使用clientid/secret进行身份验证 AllowedGrantTypes=GrantTypes.ClientCredentials, //secret认证 ClientSecrets={ new Secret("secret".Sha256())}, //客户端有权访问的范围 AllowedScopes={ "api"} } }; }
配置IdentityServer
在ConfigureServices添加AddInMemoryApiResources与AddInMemoryClients方法
public void ConfigureServices(IServiceCollection services) { // configure identity server with in-memory stores, keys, clients and resources services.AddIdentityServer() .AddDeveloperSigningCredential() .AddInMemoryApiResources(Config.GetApiResources()) .AddInMemoryClients(Config.GetClients()); }
打开 http://localhost:5000/.well-known/openid-configuration 你应该看看所谓的发现文件。您的客户端和api将使用它来下载必要的配置数据。
添加一个API项目
添加一个api项目,将url设置为 http://localhost:5001
1,添加controller
[Route("identity")] [Authorize] public class IdentityController : ControllerBase { [HttpGet] public IActionResult Get() { return new JsonResult(from c in User.Claims select new { c.Type, c.Value }); } }
2,配置
最后一步是将身份验证服务添加到DI,将身份验证中间件添加到管道。这些将会:
- 验证传入的令牌以确保它来自受信任的颁发者
- 验证该令牌是否适用于此api(又名scope)
nuget IdentityServer4.AccessTokenValidation
更新Startup如下
public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddMvcCore() .AddAuthorization() .AddJsonFormatters(); //AddAuthentication将认证服务添加到DI,并将“Bearer”配置为默认方案。 services.AddAuthentication("Bearer") //将IdentityServer访问令牌验证处理程序添加到DI中以供验证服务使用 .AddIdentityServerAuthentication(options => { options.Authority = "http://localhost:5000"; options.RequireHttpsMetadata = false; options.ApiName = "api1"; }); } public void Configure(IApplicationBuilder app) { //UseAuthentication将认证中间件添加到管道,因此每次调用主机时都会自动执行认证。 app.UseAuthentication(); app.UseMvc(); } }
如果使用浏览器导航到控制器(http://localhost:5001/identity),则应该得到401状态码作为回报。这意味着您的API需要凭据。
就是这样,API现在由IdentityServer保护。
创建client
添加一个console项目
IdentityServer中的令牌端点实现OAuth 2.0协议,您可以使用原始HTTP访问它。但是,我们有一个名为IdentityModel的客户端库,它将协议交互封装在一个易于使用的API中。
nuget IdentityModel
IdentityModel包含一个用于发现端点的客户端库。 这样您只需要知道IdentityServer的基地址 - 可以从元数据中读取实际的端点地址:
// 从元数据发现端点 var disco = await DiscoveryClient.GetAsync("http://localhost:5000"); if (disco.IsError) { Console.WriteLine(disco.Error); return; }
接下来,您可以使用TokenClient类来请求令牌。 要创建实例,您需要传入令牌端点地址,client ID和secret。
接下来,您可以使用requestcredenticlientalsasync方法为您的API请求一个令牌:
// request token var tokenClient = new TokenClient(disco.TokenEndpoint, "client", "secret"); var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1"); if (tokenResponse.IsError) { Console.WriteLine(tokenResponse.Error); return; } Console.WriteLine(tokenResponse.Json);//将access token赋值到https://jwt.io/可以查看详细信息
最后一步是调用API。
要将访问令牌发送到API,通常使用HTTP授权头。这是使用SetBearerToken扩展方法完成的:
// call api var client = new HttpClient(); client.SetBearerToken(tokenResponse.AccessToken); var response = await client.GetAsync("http://localhost:5001/identity"); if (!response.IsSuccessStatusCode) { Console.WriteLine(response.StatusCode); } else { var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(JArray.Parse(content)); }
案例下载:https://pan.baidu.com/s/1xYxymmnn6giMUphnnCGaaQ
三、使用密码保护API
客户端输入用户名和密码获取访问令牌,通过访问令牌访问资源,该规范建议仅将“资源所有者密码授予”用于“可信”应用程序
添加用户
TestUser类表示测试用户。 让我们通过将以下代码添加到我们的配置类来创建几个用户:
首先将以下using语句添加到Config.cs文件中:
using IdentityServer4.Test; public static ListGetUsers() { return new List { new TestUser { SubjectId = "1", Username = "alice", Password = "password" }, new TestUser { SubjectId = "2", Username = "bob", Password = "password" } }; }
然后向IdentityServer注册测试用户:
public void ConfigureServices(IServiceCollection services) { // configure identity server with in-memory stores, keys, clients and scopes services.AddIdentityServer() .AddDeveloperSigningCredential() .AddInMemoryApiResources(Config.GetApiResources()) .AddInMemoryClients(Config.GetClients()) .AddTestUsers(Config.GetUsers()); }
AddTestUsers扩展方法在底层做了几件事情
- 增加了对资源所有者密码授权的支持
- 增加了对登录UI通常使用的用户相关服务的支持
- 基于测试用户添加了对配置文件服务的支持
配置一个支持密码授权的客户端
public static IEnumerableGetClients() { return new List { // other clients omitted... // resource owner password grant client new Client { ClientId = "ro.client", AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, ClientSecrets = { new Secret("secret".Sha256()) }, AllowedScopes = { "api1" } } }; }
使用密码授予请求令牌
客户端看起来非常类似于我们为客户端凭据授权所做的操作。 主要区别在于客户端会以某种方式收集用户的密码,并在令牌请求期间将其发送给令牌服务。
// request token var tokenClient = new TokenClient(disco.TokenEndpoint, "ro.client", "secret"); var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync("alice", "password", "api1"); if (tokenResponse.IsError) { Console.WriteLine(tokenResponse.Error); return; } Console.WriteLine(tokenResponse.Json); Console.WriteLine("\n\n");
案例下载:https://pan.baidu.com/s/1q-UYw_34hX00MqxiZp524g
四、使用OpenID Connect添加用户验证
添加UI
OpenID Connect所需的所有协议支持都已内置到IdentityServer中。 您需要为登录,注销,同意和错误提供必要的UI部分。
虽然每个IdentityServer实现的外观和感觉以及确切的工作流程可能总是不同,但我们提供了一个基于MVC的示例UI,您可以将其用作起点。
这个用户界面可以在Quickstart UI仓库中找到。 您可以克隆或下载此repo,并将控制器,视图,模型和CSS放入您的IdentityServer Web应用程序。
创建一个MVC应用,然后将 Quickstart UI复制到项目中,nuget IdentityServer4 。并配置Startup:
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); // configure identity server with in-memory stores, keys, clients and scopes services.AddIdentityServer() .AddDeveloperSigningCredential() .AddInMemoryApiResources(Config.GetApiResources()) .AddInMemoryClients(Config.GetClients()) .AddTestUsers(Config.GetUsers()); }
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseIdentityServer(); app.UseStaticFiles(); app.UseMvcWithDefaultRoute(); }
创建一个MVC客户端
要将OpenID Connect身份验证支持添加到MVC应用程序,请将以下内容添加到启动时的ConfigureServices中:
//AddAuthentication将认证服务添加到DI services.AddAuthentication(options=>{ options.DefaultScheme="Cookies";//设置Cookies为主要认证手段 options.DefaultChallengeScheme="oidc";//当需要登录时使用OpenID Connect方案 }) .AddCookie("Cookies")//使用AddCookie添加可以处理cookie的处理程序。 .AddOpenIdConnect("oidc",options=>{ options.SignInScheme="Cookies"; options.Authority="http://localhost:5000"; options.RequireHttpsMetadata=false; options.ClientId="client"; options.SaveTokens=true; });
AddOpenIdConnect用于配置执行OpenID连接协议的处理程序。授权表明我们信任身份服务器。然后我们通过ClientId识别这个客户端。一旦OpenID连接协议完成,SignInScheme将使用cookie处理程序发出cookie。
savetoken用于将标识符从IdentityServer中持久化到cookie中(稍后需要它们)。
此外,我们关闭了JWT声明类型映射,以允许众所周知的声明(例如'sub'和'idp')通过非混淆的声明:
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
然后为了确保验证服务在每个请求上执行,请添加UseAuthentication以在启动时进行配置:
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseAuthentication();//认证中间件应该在管道中的MVC之前添加。 app.UseStaticFiles(); app.UseMvcWithDefaultRoute(); }
添加对OpenID Connect Identity Scopes的支持
与OAuth 2.0类似,OpenID Connect也使用了作用域概念。同样,作用域表示您希望保护和客户端希望访问的内容。与OAuth相反,OIDC中的作用域并不表示api,而是用户id、名称或电子邮件地址等标识数据。
在Config.cs中创建一个IdentityResource对象集合,添加对标准openid(subject id)和profile(名字,姓氏等)作用域的支持
public static IEnumerableGetIdentityResources() { return new List { new IdentityResources.OpenId(), new IdentityResources.Profile(), }; }
修改Startup
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); // configure identity server with in-memory stores, keys, clients and scopes services.AddIdentityServer() .AddDeveloperSigningCredential() .AddInMemoryIdentityResources(Config.GetIdentityResources()) .AddInMemoryApiResources(Config.GetApiResources()) .AddInMemoryClients(Config.GetClients()) .AddTestUsers(Config.GetUsers()); }
为OpenID Connect隐式流添加客户端
最后一步是将MVC客户端的新配置条目添加到IdentityServer。
基于OpenID Connect的客户端与我们迄今添加的OAuth 2.0客户端非常相似。 但由于OIDC中的流程始终是交互式的,因此我们需要将一些重定向URL添加到我们的配置中。
将以下内容添加到客户端配置中:
public static IEnumerableGetClients() { return new List { // 其他客户被忽略... // OpenID Connect隐式流程客户端(MVC) new Client { ClientId = "mvc", ClientName = "MVC Client", AllowedGrantTypes = GrantTypes.Implicit, // 登录后重定向到哪里 RedirectUris = { "http://localhost:5001/signin-oidc" }, // 注销后重定向到哪里 PostLogoutRedirectUris = { "http://localhost:5001/signout-callback-oidc" }, AllowedScopes = new List<string> { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile } } }; }
测试客户端
现在,最终所有的东西都应该适用于新的MVC客户端。
通过导航到受保护的控制器操作来触发身份验证握手。 您应该看到重定向到IdentityServer的登录页面。
案例下载:https://pan.baidu.com/s/1XHL4eRzp3oqIK3ubMF-NLQ
五、添加对外部认证的支持
接下来我们将添加对外部认证的支持。 这非常简单,因为您真正需要的只是一个兼容ASP.NET Core的身份验证处理程序。
ASP.NET Core本身也支持Google,Facebook,Twitter,Microsoft帐户和OpenID Connect。 另外你可以在这里找到许多其他认证提供者的实现。
添加Google支持
要能够使用谷歌进行身份验证,首先需要向它们注册。这是在开发人员控制台完成的。创建一个新项目,启用谷歌+ API,并通过将/signin-google路径添加到基本地址(例如http://localhost:5000/signin- Google)来配置本地标识服务器的回调地址。
如果您正在端口5000上运行——您可以使用下面代码片段中的客户端id/secret,因为这是我们预先注册的。
首先添加Google身份验证处理程序到DI。 这是通过将此片段添加到启动时的ConfigureServices中完成的:
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); // configure identity server with in-memory stores, keys, clients and scopes services.AddIdentityServer() .AddDeveloperSigningCredential() .AddInMemoryIdentityResources(Config.GetIdentityResources()) .AddInMemoryApiResources(Config.GetApiResources()) .AddInMemoryClients(Config.GetClients()) .AddTestUsers(Config.GetUsers()); services.AddAuthentication() .AddGoogle("Google", options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.ClientId = "434483408261-55tc8n0cs4ff1fe21ea8df2o443v2iuc.apps.googleusercontent.com"; options.ClientSecret = "3gcoTrEDPPJ0ukn_aYYT6PWo"; }); }
默认情况下,IdentityServer专门为外部认证的结果配置cookie处理程序(使用基于常量IdentityServerConstants.ExternalCookieAuthenticationScheme的方案)。 Google处理程序的配置然后使用该cookie处理程序。 为了更好地理解这是如何完成的,请参阅Quickstart文件夹下的AccountController类。
进一步实验
您可以添加一个额外的外部提供者。 我们有一个云托管的IdentityServer4演示版本,您可以使用OpenID Connect进行集成。
将OpenId Connect处理程序添加到DI中:
services.AddAuthentication() .AddGoogle("Google", options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.ClientId = "434483408261-55tc8n0cs4ff1fe21ea8df2o443v2iuc.apps.googleusercontent.com"; options.ClientSecret = "3gcoTrEDPPJ0ukn_aYYT6PWo"; }) .AddOpenIdConnect("oidc", "OpenID Connect", options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.SignOutScheme = IdentityServerConstants.SignoutScheme; options.Authority = "https://demo.identityserver.io/"; options.ClientId = "implicit"; options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "name", RoleClaimType = "role" }; });
六、切换到Hybrid Flow并添加API访问权限
在前面的快速启动中,我们研究了API访问和用户身份验证。现在我们要把这两个部分结合起来。
OpenID Connect & OAuth 2.0组合的美妙之处在于,您可以通过一个协议和一个与令牌服务的交换来实现。
在前面的quickstart中,我们使用了OpenID连接隐式流。在隐式流中,所有令牌都通过浏览器传输,这对于身份令牌来说是完全没问题的。现在我们还想请求一个访问令牌。
访问令牌比身份令牌更加敏感,如果不需要,我们不希望将它们暴露于“外部”世界。 OpenID Connect包含一个名为“混合流”的流程,它可以让我们两全其美,身份令牌通过浏览器通道传输,因此客户可以在做更多工作之前验证它。 如果验证成功,客户端会打开令牌服务的后端通道来检索访问令牌。
修改client配置
没有多少必要的修改。首先,我们希望允许客户端使用混合流,此外,我们还希望客户端允许执行服务器到服务器的API调用,这些调用不在用户的上下文中(这与我们的客户端凭证quickstart非常相似)。使用AllowedGrantTypes属性表示。
接下来,我们需要添加一个客户端secret。这将用于检索后通道上的访问令牌。
最后,我们还为客户端提供了对offline_access范围的访问权限——这允许为长期存在的API访问请求刷新令牌:
new Client { ClientId = "mvc", ClientName = "MVC Client", AllowedGrantTypes = GrantTypes.HybridAndClientCredentials, ClientSecrets = { new Secret("secret".Sha256()) }, RedirectUris = { "http://localhost:5002/signin-oidc" }, PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" }, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, "api1" }, AllowOfflineAccess = true };
修改MVC客户端
MVC客户端的修改也是最小的——ASP.NET核心的OpenID连接处理器对混合流有内置的支持,所以我们只需要更改一些配置值。
我们配置 ClientSecret 以匹配IdentityServer上的secret。添加 offline_access 和 api1 作用域,并将 ResponseType 设置为 code id_token (这基本上意味着“使用混合流”)
.AddOpenIdConnect("oidc", options => { options.SignInScheme = "Cookies"; options.Authority = "http://localhost:5000"; options.RequireHttpsMetadata = false; options.ClientId = "mvc"; options.ClientSecret = "secret"; options.ResponseType = "code id_token"; options.SaveTokens = true; options.GetClaimsFromUserInfoEndpoint = true; options.Scope.Add("api1"); options.Scope.Add("offline_access"); });
当您运行MVC客户端时,除了同意屏幕现在要求您提供额外的API和 offline access scope外,没有太大区别。
使用访问令牌
OpenID连接中间件为您自动保存令牌(标识、访问和刷新)。这就是savetoken设置的作用。
技术上,令牌存储在cookie的属性部分。 访问它们的最简单方法是使用Microsoft.AspNetCore.Authentication命名空间中的扩展方法。
例如在您的声明视图上:
要使用访问令牌访问API,您只需检索令牌并将其设置在您的HttpClient上:
public async TaskCallApiUsingUserAccessToken() { var accessToken = await HttpContext.GetTokenAsync("access_token"); var client = new HttpClient(); client.SetBearerToken(accessToken); var content = await client.GetStringAsync("http://localhost:5001/identity"); ViewBag.Json = JArray.Parse(content).ToString(); return View("json"); }
七、Using ASP.NET Core Identity
IdentityServer旨在提供灵活性,其中的一部分允许您为用户及其数据使用任何数据库(包括密码)。 如果你从一个新的用户数据库开始,那么ASP.NET Identity是你可以选择的一个选项。 本快速入门介绍了如何将Identity Identity与Identity Identity一起使用。
这个快速入门假设你已经完成了所有的快速入门。 快速入门使用ASP.NET Identity的方法是从Visual Studio中的ASP.NET Identity模板创建一个新项目。 这个新项目将取代之前在快速入门中从头开始构建的IdentityServer项目。 此解决方案中的所有其他项目(针对客户端和API)将保持不变。
新建ASP.NET Identity项目
第一步是为您的解决方案添加一个ASP.NET Identity的新项目。 鉴于ASP.NET身份需要大量代码,因此使用Visual Studio中的模板是有意义的。 您最终将删除IdentityServer的旧项目(假设您正在关注其他快速入门),但您需要迁移几个项目(或按照之前的快速入门中所述从头开始重写)。
nuget dotnet new mvc --auth Individual
修改hosting
不要忘记修改主机(如此处所述)以在端口5000上运行。这非常重要,因此现有客户端和API项目将继续运行。
添加IdentityServer包
添加IdentityServer4.AspNetIdentity NuGet包。 这取决于IdentityServer4包,因此会自动添加为传递依赖项。
Scopes和Clients配置
尽管这是IdentityServer的一个新项目,但我们仍需要与之前的快速入门相同的范围和客户端配置。 将用于之前快速入门的配置类(在Config.cs中)复制到此新项目中。
new Client { ClientId = "mvc", ClientName = "MVC Client", AllowedGrantTypes = GrantTypes.HybridAndClientCredentials, RequireConsent = false, ClientSecrets = { new Secret("secret".Sha256()) }, RedirectUris = { "http://localhost:5002/signin-oidc" }, PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" }, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, "api1" }, AllowOfflineAccess = true }
配置IdentityServer
与前面一样,需要在Configure reservices中和Startup.cs中配置IdentityServer。
ConfigureServices
public void ConfigureServices(IServiceCollection services) { services.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); services.AddIdentity () .AddEntityFrameworkStores () .AddDefaultTokenProviders(); // Add application services. services.AddTransient (); services.AddMvc(); // configure identity server with in-memory stores, keys, clients and scopes services.AddIdentityServer() .AddDeveloperSigningCredential() .AddInMemoryPersistedGrants() .AddInMemoryIdentityResources(Config.GetIdentityResources()) .AddInMemoryApiResources(Config.GetApiResources()) .AddInMemoryClients(Config.GetClients()) .AddAspNetIdentity (); }
Configure
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseBrowserLink(); app.UseDatabaseErrorPage(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseStaticFiles(); // app.UseAuthentication(); // not needed, since UseIdentityServer adds the authentication middleware app.UseIdentityServer(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); }
创建用户数据库
鉴于这是一个新的ASP.NET Identity项目,您将需要创建数据库。 您可以通过从项目目录运行命令提示符并运行 dotnet ef database update -c ApplicationDbContext 来执行此操作
案例下载:https://pan.baidu.com/s/1ZxVHNYApxyZMGcDg-XlTkg
八、添加一个JavaScript客户端
这个快速入门将展示如何构建JavaScript客户端应用程序。 用户将登录到IdentityServer,使用IdentityServer发出的访问令牌调用Web API,并注销IdentityServer。
添加一个JavaScript的客户端项目
添加一个空项目
添加静态文件中间件
鉴于此项目主要用于运行客户端,我们需要ASP.NET Core来提供构成我们应用程序的静态HTML和JavaScript文件。 静态文件中间件旨在执行此操作。
在Configure方法中的Startup.cs中注册静态文件中间件:
public void Configure(IApplicationBuilder app) { app.UseDefaultFiles(); app.UseStaticFiles(); }
这个中间件现在将从应用程序的〜/ wwwroot文件夹中提供静态文件。 这是我们将放置HTML和JavaScript文件的地方。
引用oidc-client
在MVC项目中,我们使用一个库来处理OpenID Connect协议。 在这个项目中,我们需要一个类似的库,除了一个在JavaScript中工作并且被设计为在浏览器中运行的库。 oidc-client库就是这样一个库。 它可以通过NPM,Bower,以及从github直接下载。
NPM
如果您想使用NPM下载oidc-client,请按照以下步骤操作:
将一个新的NPM包文件添加到您的项目中,并将其命名为package.json。
在package.json中添加一个依赖到oidc-client:
"dependencies": { "oidc-client": "1.4.1" }
一旦保存了该文件,Visual Studio应该自动将这些软件包恢复到名为node_modules的文件夹中。
在〜/node_modules/oidc-client/dist文件夹中找到名为oidc-client.js的文件,并将其复制到应用程序的〜/wwwroot文件夹中。 有更复杂的方法将您的NPM软件包复制到〜/wwwroot中,但这些技术不在本快速入门的范围之内。
添加您的HTML和JavaScript文件
在〜/wwwroot中,添加一个名为index.html和callback.html的HTML文件,并添加一个名为app.js的JavaScript文件。
index.html
这将是我们应用程序的主页面。 它将包含用于登录,注销并调用Web API的按钮的HTML。 它还将包含