asp.net core系列 58 IS4 基于瀏覽器的JavaScript客戶端應(yīng)用程序

一. 概述

本篇探討使用"基于瀏覽器的JavaScript客戶端應(yīng)用程序"看幼。與上篇實(shí)現(xiàn)功能一樣,只不過這篇使用JavaScript作為客戶端程序,而非core mvc的后臺(tái)代碼HttpClient實(shí)現(xiàn)。 功能一樣:用戶首先要登錄IdentityServer站點(diǎn),再使用IdentityServer發(fā)出的訪問令牌調(diào)用We??b API半抱,可以注銷IdentityServer站點(diǎn)下登錄的用戶,清除cookie中的令牌信息膜宋。所有這些都將來自瀏覽器中運(yùn)行的JavaScript窿侈。

此示例還是三個(gè)項(xiàng)目:

IdentityServer令牌服務(wù)項(xiàng)目 http://localhost:5000

API資源項(xiàng)目 http://localhost:5001

JavaScript客戶端項(xiàng)目 http://localhost:5003

開源Github

二. IdentityServer項(xiàng)目

1.1 定義客戶端配置
Config.cs中,定義客戶端秋茫,使用code 授權(quán)碼模式史简,即先登錄獲取code,再獲取token。項(xiàng)目其它處代碼不變肛著。

public static IEnumerable<Client> GetClients()
        {
            return new List<Client>
            {
                // JavaScript Client
                new Client
                {
                    ClientId = "js",
                    ClientName = "JavaScript Client",
                    //授權(quán)碼模式
                    AllowedGrantTypes = GrantTypes.Code,
                    //基于授權(quán)代碼的令牌是否需要驗(yàn)證密鑰,默認(rèn)為false
                    RequirePkce = true,
                    //令牌端點(diǎn)請(qǐng)求令牌時(shí)不需要客戶端密鑰
                    RequireClientSecret = false,

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

                    //指定跨域請(qǐng)求,讓IdentityServer接受這個(gè)指定網(wǎng)站的認(rèn)證請(qǐng)求圆兵。
                    AllowedCorsOrigins =     { "http://localhost:5003" },

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

三. API項(xiàng)目

在Web API項(xiàng)目中配置 跨域資源共享CORS。這將允許從http:// localhost:5003 (javascript站點(diǎn)) 到http:// localhost:5001 (API站點(diǎn)) 進(jìn)行Ajax調(diào)用(跨域)枢贿。項(xiàng)目其它處代碼不變殉农。

public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvcCore()
                .AddAuthorization()
                .AddJsonFormatters();

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

                    options.Audience = "api1";
                });

            //添加Cors服務(wù)
            services.AddCors(options =>
            {
                // this defines a CORS policy called "default"
                options.AddPolicy("default", policy =>
                {
                    policy.WithOrigins("http://localhost:5003")
                        .AllowAnyHeader()
                        .AllowAnyMethod();
                });
            });
        }
public void Configure(IApplicationBuilder app)
        {
            //添加管道
            app.UseCors("default");
            app.UseAuthentication();
            app.UseMvc();
        }

四. JavaScript客戶端項(xiàng)目

在項(xiàng)目中,所有代碼都在wwwroot下局荚,沒有涉及到服務(wù)端代碼超凳,可以完全不用core程序來調(diào)用。目錄如下所示:


其中添加了兩個(gè)html 頁(yè)(index.html, callback.html),一個(gè)app.js文件耀态,這些屬于自定義文件轮傍。oidc-client.js是核心庫(kù)。
 4.1 index頁(yè)面
 用于調(diào)用登錄首装、注銷创夜、和api。引用了oidc-client.js和app.js

<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>

4.2 app.js
是應(yīng)用程序的主要代碼仙逻,包括:登錄挥下、Api請(qǐng)求揍魂,注銷。配置與服務(wù)端代碼差不多棚瘟,如下所示:

/// <reference path="oidc-client.js" />

//消息填充
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';
    });
}

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

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",
};
//UserManager類
var mgr = new Oidc.UserManager(config);

//用戶是否登錄到JavaScript應(yīng)用程序
mgr.getUser().then(function (user) {
    if (user) {
        log("User logged in", user.profile);
    }
    else {
        log("User not logged in");
    }
});

//登錄
function login() {
    mgr.signinRedirect();
}

//跨域請(qǐng)求api
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();
}

4.3 callback.html
 用于完成與IdentityServer的OpenID Connect協(xié)議登錄握手。對(duì)應(yīng)app.js中config對(duì)象下的redirect_uri: "http://localhost:5003/callback.html"喜最。登錄完成后偎蘸,我們可以將用戶重定向回主index.html頁(yè)面。添加此代碼以完成登錄過程

<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>

五 測(cè)試

(1) 啟動(dòng)IdentityServer程序http://localhost:5000

(2) 啟動(dòng)API程序http://localhost:5001瞬内。這二個(gè)程序?qū)儆诜?wù)端

(3) 啟動(dòng)javascriptClient程序 http://localhost:5003


 (4) 用戶點(diǎn)擊login迷雪,開始握手授權(quán),重定向到IdentityServer站點(diǎn)的登錄頁(yè)

 (5) 輸入用戶的用戶名和密碼虫蝶,登錄成功章咧。跳轉(zhuǎn)到IdentityServer站點(diǎn)consent同意頁(yè)面

  (6) 點(diǎn)擊 yes allow后,跳回到客戶端站點(diǎn)http://localhost:5003/index.html能真,完成了交互式身份認(rèn)證赁严。

  (7) 調(diào)用點(diǎn)擊Call API按鈕,獲取訪問令牌粉铐,請(qǐng)求受保護(hù)的api資源疼约。調(diào)用CallAPI 時(shí),是訪問的api站點(diǎn)http://localhost:5001/identity蝙泼。

參考文獻(xiàn)

添加JavaScript客戶端

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末程剥,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子汤踏,更是在濱河造成了極大的恐慌织鲸,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件溪胶,死亡現(xiàn)場(chǎng)離奇詭異搂擦,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)载荔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門盾饮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人懒熙,你說我怎么就攤上這事丘损。” “怎么了工扎?”我有些...
    開封第一講書人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵徘钥,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我肢娘,道長(zhǎng)呈础,這世上最難降的妖魔是什么舆驶? 我笑而不...
    開封第一講書人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮而钞,結(jié)果婚禮上沙廉,老公的妹妹穿的比我還像新娘。我一直安慰自己臼节,他們只是感情好撬陵,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著网缝,像睡著了一般巨税。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上粉臊,一...
    開封第一講書人閱讀 51,590評(píng)論 1 305
  • 那天草添,我揣著相機(jī)與錄音,去河邊找鬼扼仲。 笑死远寸,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的犀盟。 我是一名探鬼主播而晒,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼阅畴!你這毒婦竟也來了倡怎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤贱枣,失蹤者是張志新(化名)和其女友劉穎监署,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體纽哥,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡钠乏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了春塌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晓避。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖只壳,靈堂內(nèi)的尸體忽然破棺而出俏拱,到底是詐尸還是另有隱情,我是刑警寧澤吼句,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布锅必,位于F島的核電站,受9級(jí)特大地震影響惕艳,放射性物質(zhì)發(fā)生泄漏搞隐。R本人自食惡果不足惜驹愚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望劣纲。 院中可真熱鬧逢捺,春花似錦、人聲如沸味廊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)余佛。三九已至,卻和暖如春窍荧,著一層夾襖步出監(jiān)牢的瞬間辉巡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工蕊退, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留郊楣,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓瓤荔,卻偏偏與公主長(zhǎng)得像净蚤,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子输硝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容