IdentityServer4 快速入门#6:添加JavaScript客户端

注意
对于任何先决条件(例如模板),请先查看概述。

本快速入门将展示如何构建基于浏览器的JavaScript客户端应用程序(有时称为“单页应用程序”或“ SPA”)。

用户将登录到IdentityServer,使用由IdentityServer发行的访问令牌调用Web API,然后注销IdentityServer。 所有这些将由浏览器中运行的JavaScript驱动。

JavaScript客户端的新项目

为JavaScript应用程序创建一个新项目。 它可以只是一个空的Web项目,一个空的ASP.NET Core应用程序或其他类似Node.js的应用程序。 本快速入门将使用ASP.NET Core应用程序。

在〜/ src目录中创建一个新的“空” ASP.NET Core Web应用程序。 您可以使用Visual Studio或从命令行执行此操作:

md JavaScriptClient
cd JavaScriptClient
dotnet new web

修改主机

修改JavaScriptClient项目以在端口5003上运行。

添加静态文件中间件

鉴于此项目旨在运行在客户端,因此我们需要ASP.NET Core要做的就是提供将构成我们的应用程序的静态HTML和JavaScript文件。 静态文件中间件就是为此目的而设计的。

Configure方法中的Startup.cs中注册静态文件中间件(并同时删除其他所有内容):

public void Configure(IApplicationBuilder app)
{
    app.UseDefaultFiles();
    app.UseStaticFiles();
}

现在,该中间件将提供应用程序〜/ wwwroot文件夹中的静态文件。 我们将在此处放置HTML和JavaScript文件。 如果您的项目中不存在该文件夹,请立即创建它。

参考oidc-client

在基于ASP.NET Core MVC的客户端项目中的先前快速入门中,我们使用一个库来处理OpenID Connect协议。 在JavaScriptClient项目的这一快速入门中,我们需要一个类似的库,只是该库可在JavaScript中运行并被设计为在浏览器中运行。 oidc-client library就是这样一种库。 它可以通过NPMBower
和从github直接下载获得。

NPM

如果要使用NPM下载oidc-client,请从JavaScriptClient项目目录运行以下命令:

npm i oidc-client
copy node_modules\oidc-client\dist\* wwwroot

这将在本地下载最新的oidc-client软件包,然后将相关的JavaScript文件复制到~/wwwroot中,以便您的应用程序可以使用它们。

说明书下载

如果您只想简单地手动下载oidc-client JavaScript文件,请浏览到GitHub存储库并下载JavaScript文件。 下载后,将它们复制到~/wwwroot,以便您的应用程序可以使用它们。

添加您的HTML和JavaScript文件

接下来是将HTML和JavaScript文件添加到~/wwwroot。 除了oidc-client.js库,我们将有两个HTML文件和一个特定于应用程序的JavaScript文件。 在~/wwwroot中,添加一个名为index.html和callback.html的HTML文件,并添加一个名为app.js的JavaScript文件。

index.html

这将是我们应用程序中的主页。 它仅包含用于用户登录,注销和调用Web API的按钮的HTML。 它还将包含<script>标记,以包含我们的两个JavaScript文件。 它还将包含用于向用户显示消息的<pre>。

它看起来应该像这样:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    <button id="login">Login</button>
    <button id="api">Call API</button>
    <button id="logout">Logout</button>

    <pre id="results"></pre>

    <script src="oidc-client.js"></script>
    <script src="app.js"></script>
</body>
</html>
app.js

这将包含我们应用程序的主要代码。 第一件事是添加一个辅助函数以将消息记录到<pre>:

function log() {
    document.getElementById('results').innerText = '';

    Array.prototype.forEach.call(arguments, function (msg) {
        if (msg instanceof Error) {
            msg = "Error: " + msg.message;
        }
        else if (typeof msg !== 'string') {
            msg = JSON.stringify(msg, null, 2);
        }
        document.getElementById('results').innerHTML += msg + '\r\n';
    });
}

接下来,添加代码以将click事件处理程序注册到三个按钮:

ocument.getElementById("login").addEventListener("click", login, false);
document.getElementById("api").addEventListener("click", api, false);
document.getElementById("logout").addEventListener("click", logout, false);

接下来,我们可以使用oidc-client库中的UserManager类来管理OpenID Connect协议。 它需要MVC客户端中必需的类似配置(尽管具有不同的值)。 添加以下代码以配置和实例化UserManager

var config = {
    authority: "http://localhost:5000",
    client_id: "js",
    redirect_uri: "http://localhost:5003/callback.html",
    response_type: "code",
    scope:"openid profile api1",
    post_logout_redirect_uri : "http://localhost:5003/index.html",
};
var mgr = new Oidc.UserManager(config);

接下来,UserManager提供一个getUser API,以了解用户是否已登录到JavaScript应用程序。 它使用JavaScript Promise异步返回结果。 返回的User对象具有一个profile文件属性,其中包含该用户的声明。 添加以下代码以检测用户是否登录到JavaScript应用程序:

mgr.getUser().then(function (user) {
    if (user) {
        log("User logged in", user.profile);
    }
    else {
        log("User not logged in");
    }
});

接下来,我们要实现loginapilogout函数。UserManager提供用于登录用户的signinRedirect和用于注销用户的signoutRedirect。 我们在上面的代码中获得的User对象还具有access_token属性,可用于对Web API进行身份验证。 access_token将通过带有承载方案的Authorization标头传递到Web API。 添加以下代码以在我们的应用程序中实现这三个功能:

function login() {
    mgr.signinRedirect();
}

function api() {
    mgr.getUser().then(function (user) {
        var url = "http://localhost:5001/identity";

        var xhr = new XMLHttpRequest();
        xhr.open("GET", url);
        xhr.onload = function () {
            log(xhr.status, JSON.parse(xhr.responseText));
        }
        xhr.setRequestHeader("Authorization", "Bearer " + user.access_token);
        xhr.send();
    });
}

function logout() {
    mgr.signoutRedirect();
}
Note

请参阅客户端凭据快速入门 有关如何创建以上代码中使用的api的信息。

callback.html

用户登录到IdentityServer后,此HTML文件就是指定的redirect_uri页面。 它将完成与IdentityServer的OpenID Connect协议登录握手。 这些代码全部由我们之前使用的UserManager类提供。 登录完成后,我们可以将用户重定向回index.html主页。 添加以下代码以完成登录过程:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    <script src="oidc-client.js"></script>
    <script>
        new Oidc.UserManager({response_mode:"query"}).signinRedirectCallback().then(function() {
            window.location = "index.html";
        }).catch(function(e) {
            console.error(e);
        });
    </script>
</body>
</html>

将客户端注册添加到IdentityServer的JavaScript客户端

现在客户端应用程序已准备就绪,我们需要在IdentityServer中为此新的JavaScript客户端定义配置条目。 在IdentityServer项目中,找到客户端配置(在Config.cs中)。 将新客户端添加到我们的新JavaScript应用程序的列表中。 它应该具有下面列出的配置:

// JavaScript Client
new Client
{
    ClientId = "js",
    ClientName = "JavaScript Client",
    AllowedGrantTypes = GrantTypes.Code,
    RequirePkce = true,
    RequireClientSecret = false,

    RedirectUris =           { "http://localhost:5003/callback.html" },
    PostLogoutRedirectUris = { "http://localhost:5003/index.html" },
    AllowedCorsOrigins =     { "http://localhost:5003" },

    AllowedScopes =
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        "api1"
    }
}

允许使用CORS对Web API进行Ajax调用

最后需要进行的配置是在Web API项目中配置CORS。 这将允许从http://localhost:5003http://localhost:5001进行Ajax调用。

配置CORS

在Startup.cs的ConfigureServices中将CORS服务添加到依赖项注入系统:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    services.AddAuthentication("Bearer")
        .AddIdentityServerAuthentication(options =>
        {
            options.Authority = "http://localhost:5000";
            options.RequireHttpsMetadata = false;

            options.ApiName = "api1";
        });

    services.AddCors(options =>
    {
        // this defines a CORS policy called "default"
        options.AddPolicy("default", policy =>
        {
            policy.WithOrigins("http://localhost:5003")
                .AllowAnyHeader()
                .AllowAnyMethod();
        });
    });
}

在配置中将CORS中间件添加到Configure:

public void Configure(IApplicationBuilder app)
{
    app.UseRouting();

    app.UseCors("default");

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

运行JavaScript应用程序

现在您应该能够运行JavaScript客户端应用程序:

image.png

单击“Login”按钮以登录用户。将用户返回到JavaScript应用程序后,您应该看到其个人资料信息:

image.png

然后单击“ API”按钮以调用Web API:

image.png

最后单击“Logout”以注销用户。

image.png

现在,您将启动一个JavaScript客户端应用程序,该应用程序使用IdentityServer进行登录,注销和验证对Web API的调用。

Source code here

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容