IdentityServer4 (4) 静默刷新(Implicit)

  • A+
所属分类:.NET技术
摘要

  git地址:https://github.com/yizhaoxian/CoreIdentityServer4Demo.git  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)》


写在前面

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、流程图

IdentityServer4 (4)  静默刷新(Implicit)

  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、页面结构目录

  IdentityServer4 (4)  静默刷新(Implicit)

三、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 再自动刷新

IdentityServer4 (4)  静默刷新(Implicit)