上兩節介紹完Hybrid模式在MVC下的使用,包括驗證從數據獲取的User和Claim對MVC的身份授權。本節將介紹Implicit模式在JavaScript應用程式中的使用,使用Node.js+Express構建JavaScript客戶端,實現前後端分離。本節授權服務和資源伺服器基於第四和第五節。 ...
上兩節介紹完Hybrid模式在MVC下的使用,包括驗證從數據獲取的User和Claim對MVC的身份授權。本節將介紹Implicit模式在JavaScript應用程式中的使用,使用Node.js+Express構建JavaScript客戶端,實現前後端分離。本節授權服務和資源伺服器基於第四和第五節。
一、使用Node.js+Express搭建JavaScript客戶端
(1)首先需要Node.js環境
下載並安裝Node.js,官網下載地址:https://nodejs.org/en/
輸入指令:node –v 檢測是否已安裝Node.js,已安裝會顯示安裝的Node.js版本
(2)安裝Express
打開cmd,輸入指令:npm install express-generator –g
輸入指令:express –h 已安裝express會顯示幫助文檔
(3)新建文件,創建JavaScript_Client應用程式
新建文件夾(在D盤新建Express文件夾),cmd進入該文件夾。
輸入:express JavaScript_Client 在當前目錄下創建一個名為JavaScript_Client的應用。目錄結構如下:
(4)安裝依賴包
輸入:cd JavaScript_Client 進入JavaScript_Client目錄
輸入:npm install 安裝依賴包
(5)啟動並測試項目
輸入:npm start
瀏覽器打開:http://localhost:3000
看到以下頁面證明成功了。
二、添加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
三、修改授權服務配置,資源伺服器允許跨域調用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