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

  • 相关阅读:
    SQL Convert的用法[转]
    C#里巧用DateTime预设一些可选的日期范围(如本年度、本季度、本月等)
    JS 操作IE游览器常用函数:window.external,history,document.execCommand
    sqoop基本应用
    SQLSever图形化界面基本使用
    hadoop 无法退出安全模式 内存够用
    Class<?> b=Class.forName(cName);
    每日学习20210925
    每日学习20210924
    hive 报错:Map local work exhausted memory
  • 原文地址:https://www.cnblogs.com/Zing/p/13425547.html
Copyright © 2020-2023  润新知