IdentityServer4之Implicit和純前端好像很配哦

前言

上一篇Resource Owner Password Credentials模式雖然有用戶參與,但對於非信任的第三方的來講,使用這種模式是有風險的,因此相對用的很少;這裏接着說說implicit隱式模式,這種模式比較適合於純前端客戶端,好比Vue、Angular、React項目等,相對來講整個流程比較安全,只需在認證服務器進行認證便可,無需在客戶端進行相關隱私信息錄入,但前提是要客戶端保證安全,否則容易被別人釣魚(安全很重要),那IdentityServer4不背這個鍋。html

正文

既然有用戶參與,交互確定少不了,認證那更不能缺乏,因此這裏使用OIDC(OpenID Connect)的協議進行用戶身份驗證;別跑,不論是客戶端仍是認證服務器端,已經都封裝好了,拿過來直接用便可;前端

從流程先了解一下,直接上圖:vue

image-20210126225530407

流程簡要說明:node

  1. 用戶經過瀏覽器(User-Agent)訪問第三方客戶端(Client);
  2. 若是客戶端須要進行認證受權,則將其重定向到認證服務器(Authorization server);
  3. 用戶(ResorceOwner)在認證服務器上進行認證;
  4. 認證服務器(Authorization server)驗證成功以後,返回AccessToken和Id Token客戶端;
  5. 後續用戶再操做客戶端的時候,若需訪問保護資源,直接在請求中帶上AccessToken便可;
  6. 最終資源服務器(Resource server)返回對應信息;

術語解釋:git

  • User-Agent:這裏指就是瀏覽器;若是客戶端是Web,就須要用戶經過瀏覽器訪問客戶端,因此這裏的瀏覽器就是User-Agent;
  • Id Token:標識用戶身份的Token,這是OpenID Connect帶上的。

知道大概的流程,接下來就經過代碼實踐的方式演示一把。github

1.拷貝上一節Reource Owner Password的受權服務器和資源服務器代碼並完善;

對於資源服務器來講,這裏先暫時不用修改其餘;ajax

認證服務器增長界面支持npm

而認證服務器須要有本身的頁面,由於在訪問客戶端時會重定向到認證服務器進行認證信息錄入和受權相關操做,那頁面須要本身寫嗎,不須要的,IdentityServer4模板中已經準備好了,以下操做:api

經過以上兩種方式均可以獲取到如下文件,將其拷貝到認證服務器項目中,以下:

image-20210126233703440

因爲以前的認證服務器只是單純API項目,不支持MVC,由於增長的界面是MVC頁面,因此須要在Startup.cs中增長MVC的支持,頁面中須要樣式文件的加載,則同時須要增長靜態文件處理,以下:

image-20210126234143337

運行起來看效果,以下:

image-20210128142756227

備案客戶端

上面界面沒問題了,這裏打算作一個純前端(Vue)的客戶端進行測試,因此須要在認證服務器中提早將客戶端進行備案,以下:

image-20210128164917702

2. 增長純前端客戶端(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;

建立純前端客戶端

項目目錄結構以下:

image-20210128090826761

簡要說明:

  • 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便可;

image-20210128144000642

3.聯合調試看效果

上一步已經將客戶端啓起來了,接下來把認證服務器和API資源服務器啓動,看看效果:

  1. 點擊Login進行登陸,就會跳轉到認證服務器進行驗證,以下:

    image-20210128095837259

    到這一步,估計用其餘瀏覽器沒什麼問題,會繼續進行跳轉;但若是用到谷歌最新的瀏覽器,這裏就不會跳轉,由於谷歌對Cookie進行整改,儘管輸入正確的用戶名和密碼,也只是刷新一下,仍是在登陸頁面,須要作一下處理,以下:

    新增一個處理類:SameSiteCookiesServiceCollectionExtensions.cs,代碼內容就不貼啦,這是公開代碼,大佬寫好的解決方案;

    寫好處理類以後,直接使用便可,以下:

    image-20210128164529597

    完成以上步驟,谷歌不能跳轉的問題就解決啦; 繼續往下走;

  2. 輸入用戶名和密碼 Zoe/123456,點擊登陸,直接進入受權選擇頁面,以下:

    image-20210128100216860

    這個界面是否是比較熟,一般咱們用微信、QQ或者是其餘登陸時,都會彈出一個受權頁面選擇受權內容。這個受權頁面是能夠控制不顯示的,只須要在認證服務器備案客戶端時設置屬性便可,以下:

    image-20210128165114127

  3. 點擊受權完成以後,就會重定向配置的callback.html頁面,在callback.html代碼中能夠看到,若是判斷用戶已經登陸,就直接轉到index.html主頁面,不然就轉到noauth.html無權限頁面,這裏已經登陸受權成功,確定最終就轉到index.html頁面,以下:

    image-20210128100623208

  4. 點擊CallAPI去調用受保護資源,內部是根據上一步獲得AccessToken進行保護資源的訪問;

    注意,這裏資源服務器必定要進跨域配置,容許客戶訪問,在API資源服務器中進行以下配置:

    image-20210128202338913

    而後運行效果以下:

    image-20210128165434044

  5. 點擊Logout登出,這裏不截圖了,小夥伴試試;

好吧,今天狀態不佳,先到這,後續項目實戰再好好說說。

源碼地址:https://github.com/zyq025/IDS4Demo/tree/main/ImlicitDemo;

總結

Implicit模式使用思路差很少就是這樣,但這種模式特別要注意項目自己安全,如數據傳輸過程被攔截,或網站自己容易被攻擊,黑客經過釣魚頁面就容易獲取隱私信息等,因此項目一般都強烈推薦https,下降數據傳輸過程被抓包的風險;而對於釣魚等這種攻擊,能夠經過判斷校驗源IP等方式;安全的點不少,這裏只是簡單舉例;下次說說Authorization Code(受權碼)模式;

一個被程序搞醜的帥小夥,關注"Code綜藝圈",跟我一塊兒學~

相關文章
相關標籤/搜索