上一篇Resource Owner Password Credentials模式雖然有用戶參與,但對於非信任的第三方的來講,使用這種模式是有風險的,因此相對用的很少;這裏接着說說implicit隱式模式,這種模式比較適合於純前端客戶端,好比Vue、Angular、React項目等,相對來講整個流程比較安全,只需在認證服務器進行認證便可,無需在客戶端進行相關隱私信息錄入,但前提是要客戶端保證安全,否則容易被別人釣魚(安全很重要),那IdentityServer4不背這個鍋。html
既然有用戶參與,交互確定少不了,認證那更不能缺乏,因此這裏使用OIDC(OpenID Connect)的協議進行用戶身份驗證;別跑,不論是客戶端仍是認證服務器端,已經都封裝好了,拿過來直接用便可;前端
從流程先了解一下,直接上圖:vue
流程簡要說明:node
術語解釋:git
知道大概的流程,接下來就經過代碼實踐的方式演示一把。github
對於資源服務器來講,這裏先暫時不用修改其餘;ajax
認證服務器增長界面支持npm
而認證服務器須要有本身的頁面,由於在訪問客戶端時會重定向到認證服務器進行認證信息錄入和受權相關操做,那頁面須要本身寫嗎,不須要的,IdentityServer4模板中已經準備好了,以下操做:api
方式一:直接從github上下載,下載地址:https://github.com/IdentityServer/IdentityServer4.Quickstart.UI/tree/main;跨域
方式二:命令行方式,從IdentityServer4模板中獲取;
dotnet new -i IdentityServer4.Templates ##先安裝模板,裏面有不少關於IDS4的模板 dotnet new is4ui ## 指定UI模板獲取,
經過以上兩種方式均可以獲取到如下文件,將其拷貝到認證服務器項目中,以下:
因爲以前的認證服務器只是單純API項目,不支持MVC,由於增長的界面是MVC頁面,因此須要在Startup.cs中增長MVC的支持,頁面中須要樣式文件的加載,則同時須要增長靜態文件處理,以下:
運行起來看效果,以下:
備案客戶端
上面界面沒問題了,這裏打算作一個純前端(Vue)的客戶端進行測試,因此須要在認證服務器中提早將客戶端進行備案,以下:
這裏不打算使用腳手架工具去生成項目,爲了避免喧賓奪主,這裏就很單純的寫html和Js,只是在其中引用Vue,用Vue的思路進行編碼演示;
既然是個純前端項目,得要有個服務器做爲宿主,而後經過URL地址訪問頁面,這裏不想用其餘比較重的服務器,live-server簡單好用,就用它了,簡單搭建一下環境:
環境準備
我是用npm安裝,因此須要安裝nodejs,這裏就不詳細擴展了,具體安裝步驟詳見:https://www.runoob.com/nodejs/nodejs-install-setup.html;
有了nodejs環境,直接npm安裝live-server,以下命令:
npm install -g live-server ###全局安裝live-server,後續均可以用
安裝完就能夠直接使用了,直接在指定目錄下執行live-server便可,默認端口是8080;
建立純前端客戶端
項目目錄結構以下:
簡要說明:
js目錄:存放依賴的js;
live-server-https目錄是提供live-server啓動時提供live-server-https支持,其實項目自己並不須要;後續若是須要將前端頁面指定https訪問,能夠指定此庫運行;
oidc-client.js/oidc-client.min.js是用於前端客戶端實現OpenID Connect,引入直接使用便可,也能夠自行在網上下載(https://github.com/IdentityModel/oidc-client-js);
vue.js提供vue的支持,雖然沒用腳手架,但用vue仍是沒問題;
callback.html登陸成功以後回調的頁面,即當在認證服務器登陸成功以後重定向客戶端的頁面;
index.html主頁面,這裏也用於登出時重定向的頁面;
noauth.html無權限時跳轉的頁面;
代碼主要集中在index.html,下面直接上代碼吧,具體說明直接看註釋吧;
index.html代碼:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> </head> <body> <div id="app"> <button id="login" @click="login">Login</button> <button id="api" @click="callApi">Call API</button> <button id="logout" @click="logout">Logout</button> <!-- 顯示日誌,包含登入、登出和請求API結果 --> <pre id="results">{{results}}</pre> </div> <script src="./js/oidc-client.js"></script> <script src="js/vue.js"></script> <!-- <script src="./js/app.js"></script> --> <script> var vm = new Vue({ el: "#app", created() { // 根據配置信息建立用戶管理對象,後續直接使用 this.mgr = new Oidc.UserManager(this.config); }, mounted() { // 這裏要注意this的使用 var that = this; this.mgr.getUser().then(function(user) { if (user) { // 判斷是否登陸成功 that.log("User logged in", user.profile); } else { // 沒登陸 that.log("User not logged in"); } }); }, data: { // 客戶端配置 config: { authority: "http://localhost:6100", client_id: "JsClient", redirect_uri: "http://localhost:8080/callback.html", response_type: "id_token token", scope: "openid profile orderApi", //客戶端須要的權限範圍 post_logout_redirect_uri: "http://localhost:8080/index.html", }, mgr: null, //登陸對象 results: "" //登陸經過數據 }, methods: { // 登陸 login: function() { console.log("login"); this.mgr.signinRedirect(); }, callApi: function() { console.log("CallAPI"); var that = this; // 先判斷是否登陸,登陸以後在去調用API this.mgr.getUser().then(function(user) { var url = "http://localhost:5000/api/Order"; // 這裏使用原生異步請求,就是引入第三方ajax庫啦 var xhr = new XMLHttpRequest(); xhr.open("GET", url); xhr.onload = function() { // 顯示請求API獲取到的數據 that.log(xhr.status, JSON.parse(xhr.responseText)); }; xhr.setRequestHeader("Authorization", "Bearer " + user.access_token); // 發送請求 xhr.send(); }); }, logout: function() { console.log("logout"); // 登出 this.mgr.signoutRedirect(); }, // 將信息展現 log: function() { var that = this; 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); } that.results = msg + '\r\n'; }); } } }); </script> </body> </html>
callback.html代碼:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> </head> <body> <script src="./js/oidc-client.js"></script> <script> new Oidc.UserManager().signinRedirectCallback().then(function() { console.log("test"); // 若是登陸成功就回到主頁面 window.location = "index.html"; }).catch(function(e) { console.log(e); //沒有權限就跳轉到無權限頁面 window.location="noauth.html"; }); </script> </body> </html>
noauth.html代碼:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> </head> <body> <div> 沒權限 </div> </body> </html>
到這客戶端的代碼已經擼完了,直接進入JsClient目錄,執行live-server便可:
若是使用http請求,執行live-server
便可;
上一步已經將客戶端啓起來了,接下來把認證服務器和API資源服務器啓動,看看效果:
點擊Login進行登陸,就會跳轉到認證服務器進行驗證,以下:
到這一步,估計用其餘瀏覽器沒什麼問題,會繼續進行跳轉;但若是用到谷歌最新的瀏覽器,這裏就不會跳轉,由於谷歌對Cookie進行整改,儘管輸入正確的用戶名和密碼,也只是刷新一下,仍是在登陸頁面,須要作一下處理,以下:
新增一個處理類:SameSiteCookiesServiceCollectionExtensions.cs,代碼內容就不貼啦,這是公開代碼,大佬寫好的解決方案;
寫好處理類以後,直接使用便可,以下:
完成以上步驟,谷歌不能跳轉的問題就解決啦; 繼續往下走;
輸入用戶名和密碼 Zoe/123456,點擊登陸,直接進入受權選擇頁面,以下:
這個界面是否是比較熟,一般咱們用微信、QQ或者是其餘登陸時,都會彈出一個受權頁面選擇受權內容。這個受權頁面是能夠控制不顯示的,只須要在認證服務器備案客戶端時設置屬性便可,以下:
點擊受權完成以後,就會重定向配置的callback.html頁面,在callback.html代碼中能夠看到,若是判斷用戶已經登陸,就直接轉到index.html主頁面,不然就轉到noauth.html無權限頁面,這裏已經登陸受權成功,確定最終就轉到index.html頁面,以下:
點擊CallAPI去調用受保護資源,內部是根據上一步獲得AccessToken進行保護資源的訪問;
注意,這裏資源服務器必定要進跨域配置,容許客戶訪問,在API資源服務器中進行以下配置:
而後運行效果以下:
點擊Logout登出,這裏不截圖了,小夥伴試試;
好吧,今天狀態不佳,先到這,後續項目實戰再好好說說。
源碼地址:https://github.com/zyq025/IDS4Demo/tree/main/ImlicitDemo;
Implicit模式使用思路差很少就是這樣,但這種模式特別要注意項目自己安全,如數據傳輸過程被攔截,或網站自己容易被攻擊,黑客經過釣魚頁面就容易獲取隱私信息等,因此項目一般都強烈推薦https,下降數據傳輸過程被抓包的風險;而對於釣魚等這種攻擊,能夠經過判斷校驗源IP等方式;安全的點不少,這裏只是簡單舉例;下次說說Authorization Code(受權碼)模式;
一個被程序搞醜的帥小夥,關注"Code綜藝圈",跟我一塊兒學~