1. 程式人生 > 程式設計 >ASP.NET Core應用JWT進行使用者認證及Token的重新整理方案

ASP.NET Core應用JWT進行使用者認證及Token的重新整理方案

目錄
  • 一、什麼是JWT?
    • 為什麼要使用JWT?
  • 二、JWT的組成:
    • Header
    • Payload
    • Signature
  • 三、認證流程
    • 四、應用例項
      • 認證服務
        • User相關:
        • TokenHelper:
      • 應用服務
      • 五、Token的重新整理

        本文將通過實際的例子來演示如何在ASP.NET Core中應用JWT進行使用者認證以及Token的重新整理方案

        一、什麼是JWT?

        JWT(on web token)基於開放標準(RFC 7519),是一種無狀態的分散式的身份驗證方式,主要用於在網路應用環境間安全地傳遞宣告。它是基於JSON的,所以它也像json一樣可以在.Net、、,、等多種語言使用。

        為什麼要使用JWT?

        傳統的Web應用一般採用Cookies+Session來進行認證。但對於目前越來越多的App、小程式等應用來說,它們對應的服務端一般都是RestFul 型別的無狀態的API,再採用這樣的的認證方式就不是很方便了。而JWT這種無狀態的分散式的身份驗證方式恰好符合這樣的需求。

        二、JWT的組成:

        JWT是什麼樣子的呢?它就是下面這樣的一段字串:

        eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjAwMiIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiLmnY7lm5siLCJuYmYiOjE1NjU5MjMxMjIsImV4cCI6MTU2NTkyMzI0MiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1NDIxNCIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6NTQyMTUifQ.Mrta7nftmfXeo_igBVd4rl2keMmm0rg0WkqRXoVAeik

        它是由三段“亂碼”字串通過兩個“.”連線在一起組成。官網https://jwt.io/提供了它的驗證方式

        它的三個字串分別對應了上圖右側的Header、Payload和Signature三部分。

        Header

        Header:
        {
        "alg": "HS256","typ": "JWT"
        }

        標識加密方式為HS256,Token型別為JWT,這段JSON通過Base64Url編碼形成上例的第一個字串

        Payload

        Payload是JWT用於資訊儲存部分,其中包含了許多種的宣告(claims)。

        可以自定義多個宣告新增到Payload中,系統也提供了一些預設的型別

        1. iss (issuer):簽發人
        2. exp (expiration time):過期時間
        3. sub (subject):主題
        4. aud (audience):受眾
        5. nbf (Not Before):生效時間
        6. iat (Issued At):簽發時間
        7. jti (JWT ID):編號

        這部分通過Base64Url編碼生成第二個字串。

        Signature

        Signature是用於Token的驗證。它的值類似這樣的表示式:Signature = HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload),secret),也就是說,它是通過將前兩個字串加密後生成的一個新字串。

        所以只有擁有同樣加密金鑰的人,才能通過前兩個字串獲得同樣的字串,通過這種方式保證了Token的真實性。

        三、認證流程

        大概的流程是這樣的:

        ASP.NETCore應用JWT進行使用者認證及Token的重新整理方案

        • 認證伺服器:用於使用者的登入驗證和Token的發放。
        • 應用伺服器:業務資料介面。被保護的API。
        • 客戶端:一般為APP、小程式等。

        認證流程:

        • 使用者首先通過登入,到認證伺服器獲取一個Token。
        • eBxaanYwIu
        • 在訪問應用伺服器的API的時候,將獲取到的Token放置在請求的Header中。
        • 應用伺服器驗證該Token,通過後返回對應的結果。

        說明:這只是示例方案,實際專案中可能有所不同。

        • 對於小型專案,可能認證服務和應用服務在一起。本例通過分開的方式來實現,使我們能更好的瞭解二者之間的認證流程。
        • 對於複雜一些的專案,可能存在多個應用服務,使用者獲取到的Token可以在多個分散式服務中被認證,這也是JWT的優勢之一。

        關於JWT的文章很多,這裡就不做過多介紹了。下面通過實際的例子來看一下 它是如何在ASP.NET Core 中應用的。

        四、應用例項

        上一節的圖:“JWT的認證流程”中涉及到客戶端、認證伺服器、應用伺服器三部分,下面通過示例來對這三部分進行模擬:

        • 認證伺服器:新建一個WebApi的解決方案,名為FlyLolo.JWT.Server。
        • 應用伺服器:新建一個WebApi的解決方案,名為FlyLolo.JWT.API。
        • 客戶端:這裡用Fiddler傳送請求做測試。

        認證服務

        首先新建一個ASP.NET Core 的解決方案WebApi的解決方案

        ASP.NETCore應用JWT進行使用者認證及Token的重新整理方案

        將其命名為FlyLolo.JWT.Server。

        首先新建一個TokenController用於登入和Token的發放:

        [Route("api/[controller]")]
        public class TokenController : Controller
        {
            private ITokenHelper tokenHelper = null;
            public TokenController(ITokenHelper _tokenHelper)
            {
                tokenHelper = _tokenHelper;
            }
            [HttpGet]
            public IActionResult Get(string code,string pwd)
            {
                User user = TemporaryData.GetUser(code);
                if (null != user && user.Password.Equals(pwd))
                {
                    return Ok(tokenHelper.CreateToken(user));
                }
                return BadRequest();
            }
        }

        它有個名為Get的Action用於接收提交的使用者名稱和密碼,並進行驗證,驗證通過後,呼叫TokenHelper的CreateToken方法生成Token返回。

        這裡涉及到了User和TokenHelper兩個類。

        User相關:

        public class User
        {
            public string Code { get; set; }
            public string Name { get; set; }
            public string Password { get; set; }
        }

        由於只是Demo,User類只含有以上三個欄位。在TemporaryData類中做了User的模擬資料

        /// <summary>
            /// 虛擬資料,模擬從或快取中讀取使用者
            /// </summary>
            public static class TemporaryData
            {
                private static List<User> Users = new List<User>() { new User { Code = "001",Name = "張三",Password = "111111" },new User { Code = "002",Name = "李四",Password = "222222" } };
        
                public static User GetUser(string code)
                {
                    return Users.FirstOrDefault(m => m.Code.Equals(code));
                }
            }

        這只是模擬資料,實際專案中應該從資料庫或者快取等讀取。

        TokenHelper:

        public class TokenHelper : ITokenHelper
            {
                private IOptions<JWTConfig> _options;
                public TokenHelper(IOptions<JWTConfig> options)
                {
                    _options = options;
                }
        
                public Token CreateToken(User user)
                {
                    Claim[] claims = { new Claim(ClaimTypes.NameIdentifier,user.Code),new Claim(ClaimTypes.Name,user.Name) };
        
                    return CreateToken(claims);
                }
                private Token CreateToken(Claim[] claims)
                {
                    var now = DateTime.Now;var expires = now.Add(TimeSpan.FromMinutes(_options.Value.AccessTokenExpiresMinutes));
                    var token = new JwtSecurityToken(
                        issuer: _options.Value.Issuer,audience: _options.Value.Audience,claims: claims,notBefore: now,expires: expires,signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_options.Value.IssuerSigningKey)),SecurityAlgorithms.HmacSha256));
                    return new Token { TokenContent = new JwtSecurityTokenHandler().WriteToken(token),Expires = expires };
                }
            }

        通過CreateToken方法建立Token,這裡有幾個關鍵引數:

        • issuer Token釋出者
        • Audience Token接受者
        • expires 過期時間
        • IssuerSigningKey 簽名祕鑰

        對應的Token程式碼如下:

        public class Token
            {
                public string TokenContent { get; set; }
        
                public DateTime Expires { get; set; }
            }

        這樣通過TokenHelper的CreateToken方法生成了一個Token返回給了客戶端。到現在來看,貌似所有的工作已經完成了。並非如此,我們還需要在Startup檔案中做一些設定。

        public class Startup
        {    // 。。。。。。此處省略部分程式碼
            public void ConfigureServices(IServiceCollection services)
            {        //讀取配置資訊
                services.AddSingleton<ITokenHelper,TokenHelper>();
                services.Configure<JWTConfig>(Configuration.GetSection("JWT"));
                //啟用JWT
                services.AddAuthentication(Options =>
                {
                    Options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    Options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                }).
                AddJwtBearer();
                services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
            }
        
            public void Configure(IApplicationBuilder app,IHostingEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }        //啟用認證中介軟體
                app.UseAuthentication();
                app.UseMvc();
            }
        }

        這裡用到了配置資訊,在appsettings.json中對認證資訊做配置如下:

        "JWT": {
            "Issuer": "FlyLolo","Audience": "TestAudience","IssuerSigningKey": "FlyLolo1234567890","AccessTokenExpiresMinutes": "30"
          }

        執行這個專案,並通過Fidder以Get方式訪問api/toeBxaanYwIuken?code=002&pwd=222222,返回結果如下:

        {"tokenContent":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjAwMiIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiLmnY7lm5siLCJuYmYiOjE1NjY3OTg0NzUsImV4cCI6MTU2NjgwMDI3NSwiaXNzIjoiRmx5TG9sbyIsImF1ZCI6IlRlc3RBdWRpZW5jZSJ9.BVf3gOuW1E9RToqKy8XXp8uIvZKL-lBA-q9fB9QTEZ4","expires":"2019-08-26T21:17:55.1183172+08:00"}

        客戶端登入成功併成功返回了一個Token,認證服務建立完成

        應用服務

        新建一個WebApi的解決方案,名為FlyLolo.JWT.API。

        新增BookController用作業務API。

        [Route("api/[controller]")]
        [Authorize]
        public class BookController : Controller
        {
            // GET: api/<controller>
            [HttpGet]
            [AllowAnonymous]
            public IEnumerable<string> Get()
            {
                return new string[] { "ASP","C#" };
            }
        
            // POST api/<controller>
            [HttpPost]
            public JsonResult Post()
            {
                return new JsonResult("Create  Book ...");
            }
        }

        對此Controller添加了[Authorize]標識,表示此Controller的Action被訪問時需要進行認證,而它的名為Get的Action被標識了[AllowAnonymous],表示此Action的訪問可以跳過認證。

        在Startup檔案中配置認證:

        public class Startup
        {
        // 省略部分程式碼
            public void ConfigureServices(IServiceCollection services)
            {
                #region 讀取配置
                JWTConfig config = new JWTConfig();
                Configuration.GetSection("JWT").Bind(config);
                #endregion
        
                #region 啟用JWT認證
                services.AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                }).
                AddJwtBearer(options =>
                {
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidIssuer = config.Issuer,ValidAudience = config.Audience,IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config.IssuerSigningKey)),ClockSkew = TimeSpan.FromMinutes(1)
                    };
                });
                #endregion
        
                services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
            }
        
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app,IHostingEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
                app.UseAuthentication();
                app.UseMvc();
            }
        }

        這裡同樣用到了配置:

        public class JWTConfig
            {
                public string Issuer { get; set; }
                public string Audience { get; set; }
                public string IssuerSigningKey { get; set; }
                public int AccessTokenExpiresMinutes { get; set; }
            }

        appsettings.json:

        "JWT": {
            "Issuer": "FlyLolo","AccessTokenExpiresMinutes": "30"
          }

        關於JWT認證,這裡通過options.TokenValidationParameters對認證資訊做了設定,ValidIssuer、ValidAudience、IssuerSigningKey這三個引數用於驗證Token生成的時候填寫的Issuer、Audience、IssuerSigningKey,所以值要和生成Token時的設定一致。

        ClockSkew預設值為5分鐘,它是一個緩衝期,例如Token設定有效期為30分鐘,到了30分鐘的時候是不會過期的,會有這麼個緩衝時間,也就是35分鐘才會過期。為了方便測試(不想等太長時間),這裡我設定了1分鐘。

        TokenValidationParameters還有一些其他引數,在它的構造方法中已經做了預設設定,程式碼如下:

        public TokenValidationParameters()
        {
            RequireExpirationTime = true;  
            RequireSignedTokens = true;    
            SaveSigninToken = false;
            ValidateActor = false;
            ValidateAudience = true;  //是否驗證接受者
            ValidateIssuer = true;   //是否驗證釋出者
            ValidateIssuerSigningKey = false;  //是否驗證祕鑰
            ValidateLifetime = true; //是否驗證過期時間
            ValidateTokenReplay = false;
         }

        訪問api/book,正常返回了結果

        ["ASP","C#"]

        通過POST方式訪問,返回401錯誤。

        這就需要使用獲取到的Toke了,如下圖方式再次訪問

        ASP.NETCore應用JWT進行使用者認證及Token的重新整理方案

        添加了“Authorization: bearer Token內容”這樣的Header,可以正常訪問了。

        至此,簡單的JWT認證示例就完成了,程式碼地址https://.com/FlyLolo/JWT.Demo/releases/tag/1.0。

        這裡可能會有個疑問,例如:

        • 1.Token被盜了怎麼辦?
          答: 在啟用Https的情況下,Token被放在Header中還是比較安全的。另外Token的有效期不要設定過長。例如可以設定為1小時(微信公眾號的開發的Token有效期為2小時)。
        • 2. Token到期瞭如何處理?
          答:理論上Token過期應該是跳到登入介面,但這樣太不友好了。可以在後臺根據Token的過期時間定期去請求新的Token。下一節來演示一下Token的重新整理方案。

        五、Token的重新整理

        為了使客戶端能夠獲取到新的Token,對上文的例子進行改造,大概思路如下:

        • 使用者登入成功的時候,一次性給他兩個Token,分別為AccessToken和RefreshToken,AccessToken用於正常請求,也就是上例中原有的Token,RefreshToken作為重新整理AccessToken的憑證。
        • AccessToken的有效期較短,例如一小時,短一點安全一些。RefreshToken有效期可以設定長一些,例如一天、一週等。
        • 當AccessToken即將過期的時候,例如提前5分鐘,客戶端利用RefreshToken請求指定的API獲取新的AccessToken並更新本地儲存中的AccessToken。

        所以只需要修改FlyLolo.JWT.Server即可。

        首先修改Token的返回方案,新增一個Model

            public class ComplexToken
            {
                public Token AccessToken { get; set; }
                public Token RefreshToken { get; set; }
            }

        包含AccessToken和RefreshToken,用於使用者登入成功後的Token結果返回。

        修改appsettings.json,新增兩個配置項:

            "RefreshTokenAudience": "RefreshTokenAudience","RefreshTokenExpiresMinutes": "10080" //60*24*7

        RefreshTokenExpiresMinutes用於設定RefreshToken的過期時間,這裡設定了7天。RefreshTokenAudience用於設定RefreshToken的接受者,與原Audience值不一致,作用是使RefreshToken不能用於訪問應用服務的業務API,而AccessToken不能用於重新整理Token。

        修改TokenHelper:

        public enum TokenType
            {
                AccessToken = 1,RefreshToken = 2
            }
            public class TokenHelper : ITokenHelper
            {
                private IOptions<JWTConfig> _options;
                public TokenHelper(IOptions<JWTConfig> options)
                {
                    _options = options;
                }
        
                public Token CreateAccessToken(User user)
                {
                    Claim[] claims = new Claim[] { new Claim(ClaimTypes.NameIdentifier,user.Name) };
        
                    return CreateToken(claims,TokenType.AccessToken);
                }
        
                public ComplexToken CreateToken(User user)
                {
                    Claim[] claims = new Claim[] { new Claim(ClaimTypes.NameIdentifier,user.Name)
                        //下面兩個Claim用於測試在Token中儲存使用者的角色資訊,對應測試在FlyLolo.JWT.API的兩個測試Controller的Put方法,若用不到可刪除,new Claim(ClaimTypes.Role,"TestPutBookRole"),"TestPutStudentRole")
                    };
        
                    return CreateToken(claims);
                }
        
                public ComplexToken CreateToken(Claim[] claims)
                {
                    return new ComplexToken { AccessToken = CreateToken(claims,TokenType.AccessToken),RefreshToken = CreateToken(claims,TokenType.RefreshToken) };
                }
        
                /// <summary>
                /// 用於建立AccessToken和RefreshToken。
                /// 這裡AccessToken和RefreshToken只是過期時間不同,【實際專案】中二者的claims內容可能會不同。
                /// 因為RefreshToken只是用於重新整理AccessToken,其內容可以簡單一些。
                /// 而AccessToken可能會附加一些其他的Claim。
                /// </summary>
                /// <param name="claims"></param>
                /// <param name="tokenType"></param>
                /// <returns></returns>
                private Token CreateToken(Claim[] claims,TokenType tokenType)
                {
                    var now = DateTime.Now;
                    var expires = now.Add(TimeSpan.FromMinutes(tokenType.Equals(TokenType.AccessToken) ? _options.Value.AccessTokenExpiresMinutes : _options.Value.RefreshTokenExpiresMinutes));//設定不同的過期時間
                    var token = new JwtSecurityToken(
                        issuer: _options.Value.Issuer,audience: tokenType.Equals(TokenType.AccessToken) ? _options.Value.Audience : _options.Value.RefreshTokenAudience,//設定不同的接受者
                        claims: claims,signingCredentials: new SigningCredentials(new SymmetreBxaanYwIuicSecurityKey(Encoding.UTF8.GetBytes(_options.Value.IssuerSigningKey)),Expires = expires };
                }
        
                public Token RefreshToken(ClaimsPrincipal claimsPrincipal)
                {
                    var code = claimsPrincipal.Claims.FirstOrDefault(m => m.Type.Equals(ClaimTypes.NameIdentifier));
                    if (null != code )
                    {
                        return CreateAccessToken(TemporaryData.GetUser(code.Value.ToString()));
                    }
                    else
                    {
                        return null;
                    }
                }
            }

        在登入後,生成兩個Token返回給客戶端。在TokenHelper添加了一個RefreshToken方法,用於生成新的AccessToken。對應在TokenController中新增一個名為Post的Action,用於呼叫這個RefreshToken方法重新整理Token

        [HttpPost]
        [Authorize]
        public IActionResult Post()
        {
            return Ok(tokenHelper.RefreshToken(Request.HttpContext.User));
        }

        這個方法添加了[Authorize]標識,說明呼叫它需要RefreshToken認證通過。既然啟用了認證,那麼在Startup檔案中需要像上例的業務API一樣做JWT的認證配置。

        public void ConfigureServices(IServiceCollection services)
                {
                    #region 讀取配置資訊
                    services.AddSingleton<ITokenHelper,TokenHelper>();
                    services.Configure<JWTConfig>(Configuration.GetSection("JWT"));
                    JWTConfig config = new JWTConfig();
                    Configuration.GetSection("JWT").Bind(config);
                    #endregion
        
                    #region 啟用JWT
                    services.AddAuthentication(Options =>
                    {
                        Options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                        Options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                    }).
                     AddJwtBearer(options =>
                     {
                         options.TokenValidationParameters = new TokenValidationParameters
                         {
                             ValidIssuer = config.Issuer,ValidAudience = config.RefreshTokenAudience,IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config.IssuerSigningKey))
                         };
                     });
                    #endregion
        
                    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
                }

        注意這裡的ValidAudience被賦值為config.RefreshTokenAudience,和FlyLolo.JWT.API中的不一致,用於防止AccessToken和RefreshToken的混用。

        再次訪問/api/token?code=002&pwd=222222,會返回兩個Token:

        {"accessToken":{"tokenContent":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjAwMiIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiLmnY7lm5siLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOlsiVGVzdFB1dEJvb2tSb2xlIiwiVGVzdFB1dFN0dWRlbnRSb2xlIl0sIm5iZiI6MTU2NjgwNjQ3OSwiZXhwIjoxNTY2ODA4Mjc5LCJpc3MiOiJGbHlMb2xvIiwiYXVkIjoiVGVzdEF1ZGllbmNlIn0.wlMorS1V0xP0Fb2MDX7jI7zsgZbb2Do3u78BAkIIwGg","expires":"2019-08-26T22:31:19.5312172+08:00"},

        "refreshToken":{"tokenContent":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjAwMiIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiLmnY7lm5siLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOlsiVGVzdFB1dEJvb2tSb2xlIiwiVGVzdFB1dFN0dWRlbnRSb2xlIl0sIm5iZiI6MTU2NjgwNjQ3OSwiZXhwIjoxNTY3NDExMjc5LCJpc3MiOiJGbHlMb2xvIiwiYXVkIjoiUmVmcmVzaFRva2VuQXVkaWVuY2UifQ.3EDi6cQBqa39-ywq2EjFGiM8W2KY5l9QAOWaIDi8FnI","expires":"2019-09-02T22:01:19.6143038+08:00"}}

        可以使用RefreshToken去請求新的AccessToken

        ASP.NETCore應用JWT進行使用者認證及Token的重新整理方案

        測試用AccessToken可以正常訪問FlyLolo.JWT.API,用RefreshToken則不可以。

        至此,Token的重新整理功能改造完成。程式碼地址:https://github.com/FlyLolo/JWT.Demo/releases/tag/1.1

        疑問:RefreshToken有效期那麼長,被盜了怎麼辦,和直接將AccessToken的有效期延長有什麼區別?

        個人認為:

        • 1. RefreshToken不像AccessToken那樣在大多數請求中都被使用。
        • 2. 應用類的API較多,對應的服務(器)也可能較多,所以洩露的概率更大一些。

        以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。