本篇探討使用"基於瀏覽器的JavaScript客戶端應用程序"。與上篇實現功能同樣,只不過這篇使用JavaScript做爲客戶端程序,而非core mvc的後臺代碼HttpClient實現。 功能同樣:用戶首先要登陸IdentityServer站點,再使用IdentityServer發出的訪問令牌調用Web API,能夠註銷IdentityServer站點下登陸的用戶,清除cookie中的令牌信息。全部這些都未來自瀏覽器中運行的JavaScript。javascript
此示例仍是三個項目:html
IdentityServer令牌服務項目 http://localhost:5000java
API資源項目 http://localhost:5001git
JavaScript客戶端項目 http://localhost:5003github
開源Githubapi
1.1 定義客戶端配置跨域
Config.cs中,定義客戶端,使用code 受權碼模式,即先登陸獲取code,再獲取token。項目其它處代碼不變。瀏覽器
public static IEnumerable<Client> GetClients() { return new List<Client> { // JavaScript Client new Client { ClientId = "js", ClientName = "JavaScript Client", //受權碼模式 AllowedGrantTypes = GrantTypes.Code, //基於受權代碼的令牌是否須要驗證密鑰,默認爲false RequirePkce = true, //令牌端點請求令牌時不須要客戶端密鑰 RequireClientSecret = false, RedirectUris = { "http://localhost:5003/callback.html" }, PostLogoutRedirectUris = { "http://localhost:5003/index.html" }, //指定跨域請求,讓IdentityServer接受這個指定網站的認證請求。 AllowedCorsOrigins = { "http://localhost:5003" }, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, "api1" } } }; }
在Web API項目中配置 跨域資源共享CORS。這將容許從http:// localhost:5003 (javascript站點) 到http:// localhost:5001 (API站點) 進行Ajax調用(跨域)。項目其它處代碼不變。cookie
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服務 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(); }
在項目中,全部代碼都在wwwroot下,沒有涉及到服務端代碼,能夠徹底不用core程序來調用。目錄以下所示:mvc
其中添加了兩個html 頁(index.html, callback.html),一個app.js文件,這些屬於自定義文件。oidc-client.js是核心庫。
4.1 index頁面
用於調用登陸、註銷、和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
是應用程序的主要代碼,包括:登陸、Api請求,註銷。配置與服務端代碼差很少,以下所示:
/// <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應用程序 mgr.getUser().then(function (user) { if (user) { log("User logged in", user.profile); } else { log("User not logged in"); } }); //登陸 function login() { mgr.signinRedirect(); } //跨域請求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協議登陸握手。對應app.js中config對象下的redirect_uri: "http://localhost:5003/callback.html"。登陸完成後,咱們能夠將用戶重定向回主index.html頁面。添加此代碼以完成登陸過程
<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>
(1) 啓動IdentityServer程序http://localhost:5000
(2) 啓動API程序http://localhost:5001。這二個程序屬於服務端
(3) 啓動javascriptClient程序 http://localhost:5003
(4) 用戶點擊login,開始握手受權,重定向到IdentityServer站點的登陸頁
(5) 輸入用戶的用戶名和密碼,登陸成功。跳轉到IdentityServer站點consent贊成頁面
(6) 點擊 yes allow後,跳回到客戶端站點http://localhost:5003/index.html,完成了交互式身份認證。
(7) 調用點擊Call API按鈕,獲取訪問令牌,請求受保護的api資源。調用CallAPI 時,是訪問的api站點http://localhost:5001/identity。
參考文獻