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、添加客户端

  1. new Client{
  2. ClientId="mvc client implicit", //客户端Id
  3. ClientName="测试客户端 Implicit", //客户端名称 随便写
  4. //Implicit 模式 因为token 是通过浏览器发送给客户端的,这里必须启用
  5. AllowAccessTokensViaBrowser=true,
  6.  
  7. AllowedGrantTypes=GrantTypes.Implicit,//验证模式
  8. RedirectUris = {
  9. "http://localhost:5003/callback.html",
  10. // AccessToken 有效期比较短,刷新 AccessToken 的页面
  11. "http://localhost:5003/silentref.html",
  12. },
  13. //是否需要用户点击同意,这里需要设置为 false,不然客户端静默刷新不可用
  14. RequireConsent=false,
  15. AllowedCorsOrigins={ "http://localhost:5003" },
  16. //注销重定向的url
  17. PostLogoutRedirectUris = { "http://localhost:5003" },
  18. AccessTokenLifetime=,
  19. //客户端访问权限
  20. AllowedScopes =
  21. {
  22. "api1",
  23. IdentityServerConstants.StandardScopes.OpenId,
  24. IdentityServerConstants.StandardScopes.Email,
  25. IdentityServerConstants.StandardScopes.Address,
  26. IdentityServerConstants.StandardScopes.Phone,
  27. IdentityServerConstants.StandardScopes.Profile
  28. }
  29. },

二、客户端

1、下载 oidc-client 库

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

2、添加测试页面

  我直接使用的/home/index 在里面添加请求授权代码

  1. <style>
  2. .box {
  3. height: 200px;
  4. overflow: auto;
  5. border: 1px solid #ccc
  6. }
  7.  
  8. .btn-box {
  9. margin-top: 10px;
  10. }
  11.  
  12. .btn-box button {
  13. margin-right: 10px;
  14. }
  15. </style>
  16. <div class="row btn-box">
  17. <button class="btn btn-primary" onclick="login()">登陆 Implicit</button>
  18. <button class="btn btn-primary" onclick="getuser()">获取 User Implicit</button>
  19. <button class="btn btn-primary" onclick="getapi()">测试 API Implicit</button>
  20. <button class="btn btn-primary" onclick="removeUser()">清除 User Implicit</button>
  21. <button class="btn btn-primary" onclick="iframeSignin()">刷新 User Implicit</button>
  22. </div>
  23. <hr />
  24. <div class="row">
  25. <h3>User:</h3>
  26. <div id="userinfo" class="col-md-12 box">
  27. </div>
  28. </div>
  29. <div class="row">
  30. <h3>API:</h3>
  31. <div id="apiresult" class="col-md-12 box">
  32. </div>
  33. </div>
  34. @section Scripts{
  35. <script src="~/lib/oidc/oidc-client.min.js"></script>
  36. <script type="text/javascript">
  37. Oidc.Log.logger = window.console;
  38. Oidc.Log.level = Oidc.Log.DEBUG;
  39. var log = function (msg) { console.log(msg); }
  40. var testconfig = {
  41. authority: "http://localhost:5002",
  42. client_id: "mvc client implicit",
  43. redirect_uri: "http://localhost:5003/callback.html",
  44. response_type: "id_token token",
  45. scope: "api1 openid email phone address profile",
  46. clockSkew: ,
  47. //启用静默刷新token
  48. silent_redirect_uri: "http://localhost:5003/silentref.html",
  49. automaticSilentRenew: true,
  50. };
  51. var mgr = new Oidc.UserManager(testconfig);
  52. mgr.events.addUserLoaded(function (user) {
  53. console.log("user loaded", user);
  54. mgr.getUser().then(function () {
  55. console.log("getUser loaded user after userLoaded event fired");
  56. });
  57. });
  58. mgr.events.addUserUnloaded(function () {
  59. console.log("user unloaded");
  60. });
  61. mgr.events.addAccessTokenExpiring(function () {
  62. log("Access token expiring..." + new Date());
  63. });
  64. mgr.events.addSilentRenewError(function (err) {
  65. log("Silent renew error: " + err.message);
  66. });
  67. mgr.events.addUserSignedOut(function () {
  68. log("User signed out of OP");
  69. mgr.removeUser();
  70. });
  71. var login = function () {
  72. mgr.signinRedirect();
  73. };
  74. var getuser = function () {
  75. mgr.getUser().then(function (user) {
  76. log("got user");
  77. $('#userinfo').html(JSON.stringify(user));
  78. }).catch(function (err) {
  79. log(err);
  80. });
  81. };
  82. var removeUser = function () {
  83. mgr.removeUser().then(function () {
  84. log("user removed");
  85. }).catch(function (err) {
  86. log(err);
  87. });
  88. }
  89. var iframeSignin = function () {
  90. mgr.signinSilent().then(function (user) {
  91. log("signed in", user);
  92. }).catch(function (err) {
  93. log(err);
  94. });
  95. }
  96. var getapi = function (token) {
  97. mgr.getUser().then(function (user) {
  98. log("get user success");
  99. document.getElementById('userinfo').innerHTML = JSON.stringify(user);
  100. var settings = {
  101. url: 'http://localhost:5001/api/suibian',
  102. beforeSend: function (xhr) {
  103. xhr.setRequestHeader('Authorization', 'Bearer ' + user.access_token)
  104. console.log("beforeSend", xhr)
  105. },
  106. success: function (res) {
  107. console.log("api result success:", res);
  108. $('#apiresult').html(JSON.stringify(res));
  109. }, error: function (res) {
  110. console.log("api result error:", res);
  111. $('#apiresult').html(res.responseText);
  112. }
  113. }
  114. $.ajax(settings);
  115.  
  116. }).catch(function (err) {
  117. log(err);
  118. });
  119. };
  120. </script>
  121. }

3、登陆回调页面

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8" />
  5. <title>Oidc-Client</title>
  6. <script src="lib/oidc/oidc-client.min.js"></script>
  7. </head>
  8. <body>
  9. 登陆中...
  10. </body>
  11. </html>
  12. <script>
  13. new Oidc.UserManager().signinRedirectCallback().then(function (user) {
  14. //console.log("signin response success");
  15. //console.log(user)
  16. //document.getElementById("message").innerText = JSON.stringify(user);
  17. location.href = "/home";
  18. }).catch(function (err) {
  19. console.log(err);
  20. });
  21. </script>

4、自动刷新页面

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8" />
  5. <title>Oidc-Client</title>
  6. </head>
  7. <body>
  8. <h1>Silent.html</h1>
  9. </body>
  10. </html>
  11. <script src="lib/oidc/oidc-client.min.js"></script>
  12. <script>
  13. new Oidc.UserManager().signinSilentCallback()
  14. .catch((err) => {
  15. console.log("refresh", err);
  16. });
  17. </script>

5、页面结构目录

  

三、API资源

1、修改StartUp.cs

ConfigureServices()

  1. services.AddCors(options =>
  2. {
  3. options.AddPolicy("client1", policy =>
  4. {
  5. //客户端地址
  6. policy.WithOrigins("http://localhost:5003");
  7. policy.AllowAnyHeader();
  8. policy.AllowAnyMethod();
  9. });
  10. });
  11.  
  12. JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
  13. services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  14. .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
  15. {
  16. // IdentityServer 地址
  17. options.Authority = "http://localhost:5002";
  18. //不需要https
  19. options.RequireHttpsMetadata = false;
  20. //这里要和 IdentityServer 定义的 api1 保持一致
  21. options.Audience = "api1";
  22. //token 默认容忍5分钟过期时间偏移,这里设置为0,
  23. //这里就是为什么定义客户端设置了过期时间为5秒,过期后仍可以访问数据
  24. options.TokenValidationParameters.ClockSkew = TimeSpan.Zero;
  25. options.Events = new JwtBearerEvents
  26. {
  27. //AccessToken 验证失败
  28. OnChallenge = op =>
  29. {
  30. //跳过所有默认操作
  31. op.HandleResponse();
  32. //下面是自定义返回消息
  33. //op.Response.Headers.Add("token", "401");
  34. op.Response.ContentType = "application/json";
  35. op.Response.StatusCode = StatusCodes.Status401Unauthorized;
  36. op.Response.WriteAsync(JsonConvert.SerializeObject(new
  37. {
  38. status = StatusCodes.Status401Unauthorized,
  39. msg = "token无效",
  40. error = op.Error
  41. }));
  42. return Task.CompletedTask;
  43. }
  44. };
  45. });

Configure()

  1. app.UseStaticFiles();
  2. //这里注意 一定要在 UseMvc前面,顺序不可改变
  3. app.UseAuthentication();
  4. app.UseCors("client1");

三、测试  

  可以看到右侧console 再自动刷新

 

posted @ 2021-03-26 11:26  dreamw  阅读(298)  评论(0编辑  收藏  举报