IdentityServer4 (4) 靜默刷新(Implicit)


寫在前面

1、源碼(.Net Core 2.2)

  git地址:https://github.com/yizhaoxian/CoreIdentityServer4Demo.git

2、相關章節

  2.1、《IdentityServer4 (1) 客戶端授權模式(Client Credentials)
  2.2、《IdentityServer4 (2) 密碼授權(Resource Owner Password)
  2.3、《IdentityServer4 (3) 授權碼模式(Authorization Code)
  2.4、《IdentityServer4 (4) 靜默刷新(Implicit)
  2.5、《IdentityServer4 (5) 混合模式(Hybrid)

3、參考資料

  IdentityServer4 中文文檔 http://www.identityserver.com.cn/
  IdentityServer4 英文文檔 https://identityserver4.readthedocs.io/en/latest/
  OpenID Connect 官網 https://openid.net/connect/
  OpenID Connect 中文 https://www.cnblogs.com/linianhui/p/openid-connect-core.html
  OpenID Connect和OAuth 2.0對比:https://www.jianshu.com/p/d453076e6433
  Oauth 2.0 官網:https://oauth.net/2/
  Oauth 2.0 授權框架:https://tools.ietf.org/html/rfc6749#section-4.2.1

4、流程圖

  1、客戶端准備一個包含所需請求參數的身份驗證請求。
  2、客戶端將請求發送到授權服務器(填寫賬號密碼)。
  3、授權服務器對最終用戶進行身份驗證(驗證賬號密碼和客戶端)。
  4、授權服務器獲得最終用戶同意/授權。
  5、授權服務器使用IdToken和AccessToken(如果要求)將最終用戶發送回客戶端。

一、服務端

1、添加客戶端

   new Client{
       ClientId="mvc client implicit", //客戶端Id
       ClientName="測試客戶端 Implicit", //客戶端名稱 隨便寫
       //Implicit 模式 因為token 是通過瀏覽器發送給客戶端的,這里必須啟用
      AllowAccessTokensViaBrowser=true,

       AllowedGrantTypes=GrantTypes.Implicit,//驗證模式 
       RedirectUris = {
           "http://localhost:5003/callback.html",
            // AccessToken 有效期比較短,刷新 AccessToken 的頁面
           "http://localhost:5003/silentref.html",
       },
       //是否需要用戶點擊同意,這里需要設置為 false,不然客戶端靜默刷新不可用
       RequireConsent=false,
       AllowedCorsOrigins={ "http://localhost:5003" },
       //注銷重定向的url
       PostLogoutRedirectUris = { "http://localhost:5003" },
       AccessTokenLifetime=10, 
       //客戶端訪問權限
       AllowedScopes =
       {
           "api1",
           IdentityServerConstants.StandardScopes.OpenId,
           IdentityServerConstants.StandardScopes.Email,
           IdentityServerConstants.StandardScopes.Address,
           IdentityServerConstants.StandardScopes.Phone,
           IdentityServerConstants.StandardScopes.Profile
       }
   },

二、客戶端

1、下載 oidc-client 庫

  git地址:https://github.com/IdentityModel/oidc-client-js 

2、添加測試頁面

  我直接使用的/home/index 在里面添加請求授權代碼

<style>
    .box {
        height: 200px;
        overflow: auto;
        border: 1px solid #ccc
    }

    .btn-box {
        margin-top: 10px;
    }

        .btn-box button {
            margin-right: 10px;
        }
</style>
<div class="row btn-box">
    <button class="btn btn-primary" onclick="login()">登陸 Implicit</button>
    <button class="btn btn-primary" onclick="getuser()">獲取 User Implicit</button>
    <button class="btn btn-primary" onclick="getapi()">測試 API Implicit</button>
    <button class="btn btn-primary" onclick="removeUser()">清除 User Implicit</button>
    <button class="btn btn-primary" onclick="iframeSignin()">刷新 User Implicit</button>
</div>
<hr />
<div class="row">
    <h3>User:</h3>
    <div id="userinfo" class="col-md-12 box">
    </div>
</div>
<div class="row">
    <h3>API:</h3>
    <div id="apiresult" class="col-md-12 box">
    </div>
</div>
@section Scripts{
    <script src="~/lib/oidc/oidc-client.min.js"></script>
    <script type="text/javascript">
        Oidc.Log.logger = window.console;
        Oidc.Log.level = Oidc.Log.DEBUG;
        var log = function (msg) { console.log(msg); }
        var testconfig = {
            authority: "http://localhost:5002",
            client_id: "mvc client implicit",
            redirect_uri: "http://localhost:5003/callback.html",
            response_type: "id_token token",
            scope: "api1 openid email phone address profile",
            clockSkew: 0,
            //啟用靜默刷新token
            silent_redirect_uri: "http://localhost:5003/silentref.html",
            automaticSilentRenew: true,
        };
        var mgr = new Oidc.UserManager(testconfig);
        mgr.events.addUserLoaded(function (user) {
            console.log("user loaded", user);
            mgr.getUser().then(function () {
                console.log("getUser loaded user after userLoaded event fired");
            });
        });
        mgr.events.addUserUnloaded(function () {
            console.log("user unloaded");
        });
        mgr.events.addAccessTokenExpiring(function () {
            log("Access token expiring..." + new Date());
        });
        mgr.events.addSilentRenewError(function (err) {
            log("Silent renew error: " + err.message);
        });
        mgr.events.addUserSignedOut(function () {
            log("User signed out of OP");
            mgr.removeUser();
        });
        var login = function () {
            mgr.signinRedirect();
        };
        var getuser = function () {
            mgr.getUser().then(function (user) {
                log("got user");
                $('#userinfo').html(JSON.stringify(user));
            }).catch(function (err) {
                log(err);
            });
        };
        var removeUser = function () {
            mgr.removeUser().then(function () {
                log("user removed");
            }).catch(function (err) {
                log(err);
            });
        }
        var iframeSignin = function () {
            mgr.signinSilent().then(function (user) {
                log("signed in", user);
            }).catch(function (err) {
                log(err);
            });
        }
        var getapi = function (token) {
            mgr.getUser().then(function (user) {
                log("get user success");
                document.getElementById('userinfo').innerHTML = JSON.stringify(user);
                var settings = {
                    url: 'http://localhost:5001/api/suibian',
                    beforeSend: function (xhr) {
                        xhr.setRequestHeader('Authorization', 'Bearer ' + user.access_token)
                        console.log("beforeSend", xhr)
                    },
                    success: function (res) {
                        console.log("api result success:", res);
                        $('#apiresult').html(JSON.stringify(res));
                    }, error: function (res) {
                        console.log("api result error:", res);
                        $('#apiresult').html(res.responseText);
                    }
                }
                $.ajax(settings);

            }).catch(function (err) {
                log(err);
            });
        }; 
    </script>
}

3、登陸回調頁面

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Oidc-Client</title>
    <script src="lib/oidc/oidc-client.min.js"></script>
</head>
<body>
    登陸中...
</body>
</html>
<script>
    new Oidc.UserManager().signinRedirectCallback().then(function (user) {
        //console.log("signin response success");
        //console.log(user)
        //document.getElementById("message").innerText = JSON.stringify(user);
        location.href = "/home";
    }).catch(function (err) {
        console.log(err);
    });
</script>

4、自動刷新頁面

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Oidc-Client</title>
</head>
<body>
    <h1>Silent.html</h1>
</body>
</html>
<script src="lib/oidc/oidc-client.min.js"></script>
<script>
    new Oidc.UserManager().signinSilentCallback()
        .catch((err) => {
            console.log("refresh", err);
        });
</script>

5、頁面結構目錄

  

三、API資源

1、修改StartUp.cs

ConfigureServices()

   services.AddCors(options =>
   {
       options.AddPolicy("client1", policy =>
       {
           //客戶端地址
           policy.WithOrigins("http://localhost:5003");
           policy.AllowAnyHeader();
           policy.AllowAnyMethod();
       });
   });

   JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
   services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
   .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
   {
       // IdentityServer 地址
       options.Authority = "http://localhost:5002";
       //不需要https
       options.RequireHttpsMetadata = false;
       //這里要和 IdentityServer 定義的 api1 保持一致
       options.Audience = "api1";
       //token 默認容忍5分鍾過期時間偏移,這里設置為0,
       //這里就是為什么定義客戶端設置了過期時間為5秒,過期后仍可以訪問數據
       options.TokenValidationParameters.ClockSkew = TimeSpan.Zero;
       options.Events = new JwtBearerEvents
       {
           //AccessToken 驗證失敗
           OnChallenge = op =>
           {
               //跳過所有默認操作
               op.HandleResponse();
               //下面是自定義返回消息
               //op.Response.Headers.Add("token", "401");
               op.Response.ContentType = "application/json";
               op.Response.StatusCode = StatusCodes.Status401Unauthorized;
               op.Response.WriteAsync(JsonConvert.SerializeObject(new
               {
                   status = StatusCodes.Status401Unauthorized,
                   msg = "token無效",
                   error = op.Error
               }));
               return Task.CompletedTask;
           }
       };
   });

Configure()

   app.UseStaticFiles();
   //這里注意 一定要在 UseMvc前面,順序不可改變
   app.UseAuthentication();
   app.UseCors("client1");

三、測試  

  可以看到右側console 再自動刷新


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM