IdentityServer4-先後端分離的受權驗證(六)

原文:IdentityServer4-先後端分離的受權驗證(六)javascript

上兩節介紹完Hybrid模式在MVC下的使用,包括驗證從數據獲取的User和Claim對MVC的身份受權。本節將介紹Implicit模式在JavaScript應用程序中的使用,使用Node.js+Express構建JavaScript客戶端,實現先後端分離。本節受權服務和資源服務器基於第四和第五節。html


 

1、使用Node.js+Express搭建JavaScript客戶端

(1)首先須要Node.js環境java

下載並安裝Node.js,官網下載地址:https://nodejs.org/en/ node

輸入指令:node –v  檢測是否已安裝Node.js,已安裝會顯示安裝的Node.js版本git

(2)安裝Expressgithub

打開cmd,輸入指令:npm install express-generator –gweb

輸入指令:express –h    已安裝express會顯示幫助文檔數據庫

(3)新建文件,建立JavaScript_Client應用程序express

新建文件夾(在D盤新建Express文件夾),cmd進入該文件夾。npm

輸入:express JavaScript_Client     在當前目錄下建立一個名爲JavaScript_Client的應用。目錄結構以下:

(4)安裝依賴包

輸入:cd JavaScript_Client   進入JavaScript_Client目錄

輸入:npm install   安裝依賴包

(5)啓動並測試項目

輸入:npm start

瀏覽器打開:http://localhost:3000 

看到如下頁面證實成功了。


 

2、添加JavaScript客戶端測試代碼

(1)安裝oidc-client庫

輸入:npm install oidc-client –save

咱們會發如今D:\express\JavaScript_Client\node_modules\oidc-client\dist  有兩個js文件

咱們只需使用這兩個文件。把這兩個文件複製到D:\express\JavaScript_Client\public\ javascripts 目錄下

(2)添加測試用的HTML文件

使用VSCode打開JavaScript_Client文件夾,在public(D:\express\JavaScript_Client\public)下新建index.html文件。添加幾個測試用的按鈕。

<!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=" javascripts/oidc-client.js"></script>
    <script src="app.js"></script>
</body>
</html> 

(3)添加測試的js文件

在public下新建app.js文件。

黏貼如下代碼

/// <reference path=" javascripts/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: "id_token token",
    scope:"openid profile api1",
    post_logout_redirect_uri : "http://localhost:5003/index.html",
};
var mgr = new Oidc.UserManager(config);

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

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();
}
View Code

如下對app.js代碼進行分析

App.js中log函數用來記錄消息

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';
    });
}

使用oidc-client庫中的UserManager類來管理OpenID鏈接協議。添加此代碼以配置和實例化UserManager:

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

接下來,UserManager提供一個getUser API來獲取用戶是否登陸到JavaScript應用程序。返回的User對象有一個profile屬性,其中包含用戶的聲明。添加此代碼以檢測用戶是否登陸到JavaScript應用程序:

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

接下來,咱們要實現登陸、api和註銷功能。UserManager提供登陸用戶的signinRedirect和用戶登出的signoutRedirect。咱們在上述代碼中得到的用戶對象還有一個access_token屬性,可使用該屬性對web API進行身份驗證。access_token將經過Bearer模式傳遞給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();
}

(4)再新建一個callback.html。一旦用戶登陸到IdentityServer,這個HTML文件就是指定的redirect_uri頁面。它將完成OpenID Connect協議與IdentityServer的登陸握手。這裏的代碼都由咱們前面使用的UserManager類提供。登陸完成後,咱們能夠將用戶重定向回index.html頁面。添加此代碼完成登陸過程:

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

 

(8)修改服務端口爲5003


 

3、修改受權服務配置,資源服務器容許跨域調用API

(1)修改受權服務配置

在AuthServer項目,打開Config.cs文件,在GetClients中添加JavaScript客戶端配置

                // JavaScript Client
                new Client
                {
                    ClientId = "js",
                    ClientName = "JavaScript Client",
                    AllowedGrantTypes = GrantTypes.Implicit,
                    AllowAccessTokensViaBrowser = true,

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

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

(2)在資源服務配置容許跨域調用api

在ResourceAPI項目,打開Startup.cs文件中的ConfigureServices方法,配置CORS,容許Ajax調用從http://localhost:5003調用http://localhost:5001的Web API。

 //JS-allow Ajax calls to be made from http://localhost:5003 to http://localhost:5001.
            services.AddCors(options =>
            {
                //this defines a CORS policy called "default"
                options.AddPolicy("default", policy =>
                {
                    policy.WithOrigins("http://localhost:5003")
                        .AllowAnyHeader()
                        .AllowAnyMethod();
                });
            });

在Configure方法中將CORS中間件添加到管道中

 //JS-Add the CORS middleware to the pipeline in Configure:

            app.UseCors("default");

(3)添加測試用的api接口

添加IdentityController控制器

[Route("[controller]")]
    public class IdentityController : ControllerBase
    {
        [Authorize(Roles ="admin")]
        [HttpGet]
        public IActionResult Get()
        {
            return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
        }
    }

(4)測試

運行AuthServer項目,運行ResourceAPI項目。

在VSCode終端輸入:npm start

打開瀏覽器:http://localhost:5003/

點擊Login,使用帳號:zhubingjian 密碼:123  登陸

登陸返回用戶的Claims信息

點擊Call API,調用資源服務器的API接口

成功獲取接口返回的信息。


經過這六節的內容,大概地介紹了IdentityServer4中Client的應用場景,包括MVC、先後端分離和服務端。

此外還介紹瞭如何動態配置Client、如何驗證從數據庫中獲取的User以及自定義Claims的方法。

這個系列對IdentityServer4的介紹也是我博客的起點,寫博客雖然很花時間,可是能夠幫助我加深對知識點的理解。然而文中也體現到我對某些知識點的理解仍是不到位的,望你們見諒。

參考官網地址:https://identityserver4.readthedocs.io/en/release/quickstarts/7_javascript_client.html

受權服務和資源服務源碼地址:https://github.com/Bingjian-Zhu/Mvc-HybridFlow.git

JavaScript客戶端源碼地址:https://github.com/Bingjian-Zhu/Identity-JavaScript_Client.git

相關文章
相關標籤/搜索