","marks":[]}]}]},{"type":"block","id":"ywmW-1739351860678","name":"code-line","data":{},"nodes":[{"type":"text","id":"aYUj-1739351860677","leaves":[{"text":" {","marks":[]}]}]},{"type":"block","id":"LBFQ-1739351860682","name":"code-line","data":{},"nodes":[{"type":"text","id":"MlUO-1739351860681","leaves":[{"text":" ..","marks":[]}]}]},{"type":"block","id":"kKpI-1739351860686","name":"code-line","data":{},"nodes":[{"type":"text","id":"cdsQ-1739351860685","leaves":[{"text":" options.Events = new JwtBearerEvents","marks":[]}]}]},{"type":"block","id":"zzAq-1739351860688","name":"code-line","data":{},"nodes":[{"type":"text","id":"zZcV-1739351860687","leaves":[{"text":" {","marks":[]}]}]},{"type":"block","id":"OxBI-1739351860690","name":"code-line","data":{},"nodes":[{"type":"text","id":"gupv-1739351860689","leaves":[{"text":" OnChallenge = context =>","marks":[]}]}]},{"type":"block","id":"iqEE-1739351860693","name":"code-line","data":{},"nodes":[{"type":"text","id":"gbhH-1739351860692","leaves":[{"text":" {","marks":[]}]}]},{"type":"block","id":"OjD8-1739351860695","name":"code-line","data":{},"nodes":[{"type":"text","id":"BhK2-1739351860694","leaves":[{"text":" if (context.AuthenticateFailure.GetType() == typeof(SecurityTokenExpiredException))","marks":[]}]}]},{"type":"block","id":"4fh6-1739351860697","name":"code-line","data":{},"nodes":[{"type":"text","id":"8HsT-1739351860696","leaves":[{"text":" {","marks":[]}]}]},{"type":"block","id":"SsvG-1739351860701","name":"code-line","data":{},"nodes":[{"type":"text","id":"UGWu-1739351860700","leaves":[{"text":" context.HandleResponse();","marks":[]}]}]},{"type":"block","id":"xU2T-1739351860703","name":"code-line","data":{},"nodes":[{"type":"text","id":"gPha-1739351860702","leaves":[{"text":" return context.Response.WriteAsync(GetTokenExpiredResponse(context.Response));","marks":[]}]}]},{"type":"block","id":"pp28-1739351860705","name":"code-line","data":{},"nodes":[{"type":"text","id":"fJPi-1739351860704","leaves":[{"text":" }","marks":[]}]}]},{"type":"block","id":"04lF-1739351860707","name":"code-line","data":{},"nodes":[{"type":"text","id":"JaKS-1739351860706","leaves":[{"text":" return Task.CompletedTask;","marks":[]}]}]},{"type":"block","id":"hcGL-1739351860709","name":"code-line","data":{},"nodes":[{"type":"text","id":"H2Lg-1739351860708","leaves":[{"text":" },","marks":[]}]}]},{"type":"block","id":"LEiw-1739351860713","name":"code-line","data":{},"nodes":[{"type":"text","id":"xPFs-1739351860712","leaves":[{"text":" OnTokenValidated = async context =>","marks":[]}]}]},{"type":"block","id":"3RPQ-1739351860715","name":"code-line","data":{},"nodes":[{"type":"text","id":"h47S-1739351860714","leaves":[{"text":" {","marks":[]}]}]},{"type":"block","id":"0aQc-1739351860717","name":"code-line","data":{},"nodes":[{"type":"text","id":"6ze4-1739351860716","leaves":[{"text":" if (await IsAccessTokenExpired(context, services))","marks":[]}]}]},{"type":"block","id":"ABMl-1739351860719","name":"code-line","data":{},"nodes":[{"type":"text","id":"1eb7-1739351860718","leaves":[{"text":" {","marks":[]}]}]},{"type":"block","id":"3vHB-1739351860721","name":"code-line","data":{},"nodes":[{"type":"text","id":"tHAY-1739351860720","leaves":[{"text":" Log.Information($\"The access token is expired as user already signed out or changed password.\");","marks":[]}]}]},{"type":"block","id":"LU2H-1739351860723","name":"code-line","data":{},"nodes":[{"type":"text","id":"vIAT-1739351860722","leaves":[{"text":" context.Fail(GetTokenExpiredResponse(context.Response));","marks":[]}]}]},{"type":"block","id":"dZuM-1739351860725","name":"code-line","data":{},"nodes":[{"type":"text","id":"MHwg-1739351860724","leaves":[{"text":" }","marks":[]}]}]},{"type":"block","id":"yNdx-1739351860727","name":"code-line","data":{},"nodes":[{"type":"text","id":"qcIj-1739351860726","leaves":[{"text":" await Task.CompletedTask;","marks":[]}]}]},{"type":"block","id":"cd9M-1739351860729","name":"code-line","data":{},"nodes":[{"type":"text","id":"KijG-1739351860728","leaves":[{"text":" }","marks":[]}]}]},{"type":"block","id":"NzfQ-1739351860731","name":"code-line","data":{},"nodes":[{"type":"text","id":"Xmyz-1739351860730","leaves":[{"text":" };","marks":[]}]}]},{"type":"block","id":"8W5v-1739351860733","name":"code-line","data":{},"nodes":[{"type":"text","id":"RQNZ-1739351860732","leaves":[{"text":" });","marks":[]}]}]}],"state":{"tokensPerLine":[[{"type":"plain","content":" services"},{"type":"punctuation","content":"."},{"type":"function","content":"AddAuthentication"},{"type":"punctuation","content":"("},{"type":"plain","content":"JwtBearerDefaults"},{"type":"punctuation","content":"."},{"type":"plain","content":"AuthenticationScheme"},{"type":"punctuation","content":")"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"."},{"type":"function","content":"AddJwtBearer"},{"type":"punctuation","content":"("},{"type":"plain","content":"options "},{"type":"operator","content":"=>"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"{"}],[{"type":"plain","content":" "},{"type":"range","content":".."}],[{"type":"plain","content":" options"},{"type":"punctuation","content":"."},{"type":"plain","content":"Events "},{"type":"operator","content":"="},{"type":"plain","content":" "},{"type":"keyword","content":"new"},{"type":"plain","content":" "},{"type":"constructor-invocation","content":"JwtBearerEvents"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"{"}],[{"type":"plain","content":" OnChallenge "},{"type":"operator","content":"="},{"type":"plain","content":" context "},{"type":"operator","content":"=>"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"{"}],[{"type":"plain","content":" "},{"type":"keyword","content":"if"},{"type":"plain","content":" "},{"type":"punctuation","content":"("},{"type":"plain","content":"context"},{"type":"punctuation","content":"."},{"type":"plain","content":"AuthenticateFailure"},{"type":"punctuation","content":"."},{"type":"function","content":"GetType"},{"type":"punctuation","content":"("},{"type":"punctuation","content":")"},{"type":"plain","content":" "},{"type":"operator","content":"=="},{"type":"plain","content":" "},{"type":"keyword","content":"typeof"},{"type":"punctuation","content":"("},{"type":"type-expression","content":"SecurityTokenExpiredException"},{"type":"punctuation","content":")"},{"type":"punctuation","content":")"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"{"}],[{"type":"plain","content":" context"},{"type":"punctuation","content":"."},{"type":"function","content":"HandleResponse"},{"type":"punctuation","content":"("},{"type":"punctuation","content":")"},{"type":"punctuation","content":";"}],[{"type":"plain","content":" "},{"type":"keyword","content":"return"},{"type":"plain","content":" context"},{"type":"punctuation","content":"."},{"type":"plain","content":"Response"},{"type":"punctuation","content":"."},{"type":"function","content":"WriteAsync"},{"type":"punctuation","content":"("},{"type":"function","content":"GetTokenExpiredResponse"},{"type":"punctuation","content":"("},{"type":"plain","content":"context"},{"type":"punctuation","content":"."},{"type":"plain","content":"Response"},{"type":"punctuation","content":")"},{"type":"punctuation","content":")"},{"type":"punctuation","content":";"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"}"}],[{"type":"plain","content":" "},{"type":"keyword","content":"return"},{"type":"plain","content":" Task"},{"type":"punctuation","content":"."},{"type":"plain","content":"CompletedTask"},{"type":"punctuation","content":";"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"}"},{"type":"punctuation","content":","}],[{"type":"plain","content":" OnTokenValidated "},{"type":"operator","content":"="},{"type":"plain","content":" "},{"type":"keyword","content":"async"},{"type":"plain","content":" context "},{"type":"operator","content":"=>"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"{"}],[{"type":"plain","content":" "},{"type":"keyword","content":"if"},{"type":"plain","content":" "},{"type":"punctuation","content":"("},{"type":"keyword","content":"await"},{"type":"plain","content":" "},{"type":"function","content":"IsAccessTokenExpired"},{"type":"punctuation","content":"("},{"type":"plain","content":"context"},{"type":"punctuation","content":","},{"type":"plain","content":" services"},{"type":"punctuation","content":")"},{"type":"punctuation","content":")"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"{"}],[{"type":"plain","content":" Log"},{"type":"punctuation","content":"."},{"type":"function","content":"Information"},{"type":"punctuation","content":"("},{"type":"string","content":"$\"The access token is expired as user already signed out or changed password.\""},{"type":"punctuation","content":")"},{"type":"punctuation","content":";"}],[{"type":"plain","content":" context"},{"type":"punctuation","content":"."},{"type":"function","content":"Fail"},{"type":"punctuation","content":"("},{"type":"function","content":"GetTokenExpiredResponse"},{"type":"punctuation","content":"("},{"type":"plain","content":"context"},{"type":"punctuation","content":"."},{"type":"plain","content":"Response"},{"type":"punctuation","content":")"},{"type":"punctuation","content":")"},{"type":"punctuation","content":";"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"}"}],[{"type":"plain","content":" "},{"type":"keyword","content":"await"},{"type":"plain","content":" Task"},{"type":"punctuation","content":"."},{"type":"plain","content":"CompletedTask"},{"type":"punctuation","content":";"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"}"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"}"},{"type":"punctuation","content":";"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"}"},{"type":"punctuation","content":")"},{"type":"punctuation","content":";"}]]}},{"type":"block","id":"Ziyt-1739256961743","name":"paragraph","data":{},"nodes":[{"type":"text","id":"YbaO-1739256961742","leaves":[{"text":"","marks":[]}]}],"state":{}},{"type":"block","id":"LqQo-1739351937871","name":"code","data":{"version":1,"language":"csharp","theme":"default"},"nodes":[{"type":"block","id":"QyU0-1739351937794","name":"code-line","data":{},"nodes":[{"type":"text","id":"2hSQ-1739351937793","leaves":[{"text":" private string GetTokenExpiredResponse(HttpResponse response)","marks":[]}]}]},{"type":"block","id":"EyFE-1739351937796","name":"code-line","data":{},"nodes":[{"type":"text","id":"QAF7-1739351937795","leaves":[{"text":" {","marks":[]}]}]},{"type":"block","id":"KoZw-1739351937798","name":"code-line","data":{},"nodes":[{"type":"text","id":"xdTe-1739351937797","leaves":[{"text":" if (ApiResponseCodes.AccessTokenExpired.BuildHttpResponse() is ObjectResult result)","marks":[]}]}]},{"type":"block","id":"cQRN-1739351937800","name":"code-line","data":{},"nodes":[{"type":"text","id":"M0DF-1739351937799","leaves":[{"text":" {","marks":[]}]}]},{"type":"block","id":"Qm1p-1739351937802","name":"code-line","data":{},"nodes":[{"type":"text","id":"CCVi-1739351937801","leaves":[{"text":" var payload = JObject.FromObject(result.Value);","marks":[]}]}]},{"type":"block","id":"fLRf-1739351937804","name":"code-line","data":{},"nodes":[{"type":"text","id":"8DYT-1739351937803","leaves":[{"text":" response.ContentType = \"application/json\";","marks":[]}]}]},{"type":"block","id":"KLLD-1739351937806","name":"code-line","data":{},"nodes":[{"type":"text","id":"1w1i-1739351937805","leaves":[{"text":" response.StatusCode = 401;","marks":[]}]}]},{"type":"block","id":"rv37-1739351937808","name":"code-line","data":{},"nodes":[{"type":"text","id":"HFTh-1739351937807","leaves":[{"text":"","marks":[]}]}]},{"type":"block","id":"pVBq-1739351937810","name":"code-line","data":{},"nodes":[{"type":"text","id":"SFa4-1739351937809","leaves":[{"text":" return payload.ToString();","marks":[]}]}]},{"type":"block","id":"aOFk-1739351937812","name":"code-line","data":{},"nodes":[{"type":"text","id":"4kTy-1739351937811","leaves":[{"text":" }","marks":[]}]}]},{"type":"block","id":"mnt9-1739351937814","name":"code-line","data":{},"nodes":[{"type":"text","id":"jd0X-1739351937813","leaves":[{"text":" return string.Empty;","marks":[]}]}]},{"type":"block","id":"OEUF-1739351937816","name":"code-line","data":{},"nodes":[{"type":"text","id":"Jo1C-1739351937815","leaves":[{"text":" }","marks":[]}]}]},{"type":"block","id":"5Y91-1739351937818","name":"code-line","data":{},"nodes":[{"type":"text","id":"2xtP-1739351937817","leaves":[{"text":"","marks":[]}]}]},{"type":"block","id":"XMJG-1741097487783","name":"code-line","data":{},"nodes":[{"type":"text","id":"7Txg-1741097487782","leaves":[{"text":"/* ","marks":[]}]}]},{"type":"block","id":"zjRX-1741097556349","name":"code-line","data":{},"nodes":[{"type":"text","id":"SM6B-1741097556347","leaves":[{"text":" // the instance IRedisCacheService should get by context.HttpContext.RequestServices.GetRequiredService","marks":[]}]}]},{"type":"block","id":"lc13-1739351937820","name":"code-line","data":{},"nodes":[{"type":"text","id":"Zh3V-1739351937819","leaves":[{"text":" private IRedisCacheService _redisService;","marks":[]}]}]},{"type":"block","id":"h8f0-1739351937822","name":"code-line","data":{},"nodes":[{"type":"text","id":"xSWX-1739351937821","leaves":[{"text":" private IRedisCacheService GetRedisService(IServiceCollection services)","marks":[]}]}]},{"type":"block","id":"bQKC-1739351937824","name":"code-line","data":{},"nodes":[{"type":"text","id":"gluM-1739351937823","leaves":[{"text":" {","marks":[]}]}]},{"type":"block","id":"H78c-1739351937826","name":"code-line","data":{},"nodes":[{"type":"text","id":" 3JL-1739351937825","leaves":[{"text":" if (_redisService == null)","marks":[]}]}]},{"type":"block","id":"dgSt-1739351937828","name":"code-line","data":{},"nodes":[{"type":"text","id":"Y05R-1739351937827","leaves":[{"text":" {","marks":[]}]}]},{"type":"block","id":"H59e-1739351937830","name":"code-line","data":{},"nodes":[{"type":"text","id":"oXXM-1739351937829","leaves":[{"text":" var serviceProvider = services.BuildServiceProvider();","marks":[]}]}]},{"type":"block","id":"6PTt-1739351937832","name":"code-line","data":{},"nodes":[{"type":"text","id":"U5uE-1739351937831","leaves":[{"text":" _redisService = serviceProvider.GetService();","marks":[]}]}]},{"type":"block","id":"O2E0-1739351937834","name":"code-line","data":{},"nodes":[{"type":"text","id":"ZRUA-1739351937833","leaves":[{"text":" }","marks":[]}]}]},{"type":"block","id":"agAs-1739351937836","name":"code-line","data":{},"nodes":[{"type":"text","id":"WvZ7-1739351937835","leaves":[{"text":" return _redisService;","marks":[]}]}]},{"type":"block","id":"2rVf-1739351937838","name":"code-line","data":{},"nodes":[{"type":"text","id":"q4T1-1739351937837","leaves":[{"text":" }","marks":[]}]}]},{"type":"block","id":"dpbP-1741097497321","name":"code-line","data":{},"nodes":[{"type":"text","id":"Wigi-1741097497319","leaves":[{"text":"*/","marks":[]}]}]},{"type":"block","id":"x1CJ-1741097560365","name":"code-line","data":{},"nodes":[{"type":"text","id":"Dyjo-1741097560363","leaves":[{"text":"","marks":[]}]}]},{"type":"block","id":"deDi-1739351937840","name":"code-line","data":{},"nodes":[{"type":"text","id":"J6bW-1739351937839","leaves":[{"text":" private async Task IsAccessTokenExpired(TokenValidatedContext context, IServiceCollection services)","marks":[]}]}]},{"type":"block","id":"UfXa-1739351937842","name":"code-line","data":{},"nodes":[{"type":"text","id":"C8j5-1739351937841","leaves":[{"text":" {","marks":[]}]}]},{"type":"block","id":"Adkv-1739351937844","name":"code-line","data":{},"nodes":[{"type":"text","id":"l3jM-1739351937843","leaves":[{"text":" try","marks":[]}]}]},{"type":"block","id":"YUwV-1739351937846","name":"code-line","data":{},"nodes":[{"type":"text","id":"BSTH-1739351937845","leaves":[{"text":" {","marks":[]}]}]},{"type":"block","id":"QocC-1739351937848","name":"code-line","data":{},"nodes":[{"type":"text","id":"dEqE-1739351937847","leaves":[{"text":" var requestHeader = context.Request.Headers[\"Authorization\"];","marks":[]}]}]},{"type":"block","id":"z888-1739351937850","name":"code-line","data":{},"nodes":[{"type":"text","id":"wMcJ-1739351937849","leaves":[{"text":" var accessToken = requestHeader.Count > 0 ? requestHeader[0].Split(\" \")[1] : String.Empty;","marks":[]}]}]},{"type":"block","id":"10YY-1739351937852","name":"code-line","data":{},"nodes":[{"type":"text","id":"h15W-1739351937851","leaves":[{"text":" var redisService = context.HttpContext.RequestServices.GetRequiredService();//GetRedisService(services);","marks":[]}]}]},{"type":"block","id":"qeVB-1739351937854","name":"code-line","data":{},"nodes":[{"type":"text","id":"LY27-1739351937853","leaves":[{"text":" var blackToken = await redisService.GetCache(TokenHelper.GetRedisKeyForBlackAccessToken(accessToken));","marks":[]}]}]},{"type":"block","id":"NXgl-1739351937856","name":"code-line","data":{},"nodes":[{"type":"text","id":"ZmcG-1739351937855","leaves":[{"text":" return blackToken == accessToken;","marks":[]}]}]},{"type":"block","id":"Krut-1739351937858","name":"code-line","data":{},"nodes":[{"type":"text","id":"Gv0X-1739351937857","leaves":[{"text":" }","marks":[]}]}]},{"type":"block","id":"ljPq-1739351937860","name":"code-line","data":{},"nodes":[{"type":"text","id":"qUV4-1739351937859","leaves":[{"text":" catch (Exception ex)","marks":[]}]}]},{"type":"block","id":"tC03-1739351937862","name":"code-line","data":{},"nodes":[{"type":"text","id":"TcJo-1739351937861","leaves":[{"text":" {","marks":[]}]}]},{"type":"block","id":"8nr6-1739351937864","name":"code-line","data":{},"nodes":[{"type":"text","id":"70dc-1739351937863","leaves":[{"text":" Log.Error(ex, $\"Failed to validate access token: {ex.Message}\");","marks":[]}]}]},{"type":"block","id":"iAyO-1739351937866","name":"code-line","data":{},"nodes":[{"type":"text","id":"Wo9M-1739351937865","leaves":[{"text":" return true;","marks":[]}]}]},{"type":"block","id":"SdQL-1739351937868","name":"code-line","data":{},"nodes":[{"type":"text","id":"rSej-1739351937867","leaves":[{"text":" }","marks":[]}]}]},{"type":"block","id":"2bA3-1739351937870","name":"code-line","data":{},"nodes":[{"type":"text","id":"wvtK-1739351937869","leaves":[{"text":" }","marks":[]}]}]}],"state":{"tokensPerLine":[[{"type":"plain","content":" "},{"type":"keyword","content":"private"},{"type":"plain","content":" "},{"type":"keyword","content":"string"},{"type":"plain","content":" "},{"type":"function","content":"GetTokenExpiredResponse"},{"type":"punctuation","content":"("},{"type":"class-name","content":"HttpResponse"},{"type":"plain","content":" response"},{"type":"punctuation","content":")"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"{"}],[{"type":"plain","content":" "},{"type":"keyword","content":"if"},{"type":"plain","content":" "},{"type":"punctuation","content":"("},{"type":"plain","content":"ApiResponseCodes"},{"type":"punctuation","content":"."},{"type":"plain","content":"AccessTokenExpired"},{"type":"punctuation","content":"."},{"type":"function","content":"BuildHttpResponse"},{"type":"punctuation","content":"("},{"type":"punctuation","content":")"},{"type":"plain","content":" "},{"type":"keyword","content":"is"},{"type":"plain","content":" "},{"type":"class-name","content":"ObjectResult"},{"type":"plain","content":" result"},{"type":"punctuation","content":")"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"{"}],[{"type":"plain","content":" "},{"type":"keyword","content":"var"},{"type":"plain","content":" payload "},{"type":"operator","content":"="},{"type":"plain","content":" JObject"},{"type":"punctuation","content":"."},{"type":"function","content":"FromObject"},{"type":"punctuation","content":"("},{"type":"plain","content":"result"},{"type":"punctuation","content":"."},{"type":"plain","content":"Value"},{"type":"punctuation","content":")"},{"type":"punctuation","content":";"}],[{"type":"plain","content":" response"},{"type":"punctuation","content":"."},{"type":"plain","content":"ContentType "},{"type":"operator","content":"="},{"type":"plain","content":" "},{"type":"string","content":"\"application/json\""},{"type":"punctuation","content":";"}],[{"type":"plain","content":" response"},{"type":"punctuation","content":"."},{"type":"plain","content":"StatusCode "},{"type":"operator","content":"="},{"type":"plain","content":" "},{"type":"number","content":"401"},{"type":"punctuation","content":";"}],[],[{"type":"plain","content":" "},{"type":"keyword","content":"return"},{"type":"plain","content":" payload"},{"type":"punctuation","content":"."},{"type":"function","content":"ToString"},{"type":"punctuation","content":"("},{"type":"punctuation","content":")"},{"type":"punctuation","content":";"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"}"}],[{"type":"plain","content":" "},{"type":"keyword","content":"return"},{"type":"plain","content":" "},{"type":"keyword","content":"string"},{"type":"punctuation","content":"."},{"type":"plain","content":"Empty"},{"type":"punctuation","content":";"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"}"}],[],[{"type":"comment","content":"/* "}],[{"type":"comment","content":" // the instance IRedisCacheService should get by context.HttpContext.RequestServices.GetRequiredService"}],[{"type":"comment","content":" private IRedisCacheService _redisService;"}],[{"type":"comment","content":" private IRedisCacheService GetRedisService(IServiceCollection services)"}],[{"type":"comment","content":" {"}],[{"type":"comment","content":" if (_redisService == null)"}],[{"type":"comment","content":" {"}],[{"type":"comment","content":" var serviceProvider = services.BuildServiceProvider();"}],[{"type":"comment","content":" _redisService = serviceProvider.GetService();"}],[{"type":"comment","content":" }"}],[{"type":"comment","content":" return _redisService;"}],[{"type":"comment","content":" }"}],[{"type":"comment","content":"*/"}],[],[{"type":"plain","content":" "},{"type":"keyword","content":"private"},{"type":"plain","content":" "},{"type":"keyword","content":"async"},{"type":"plain","content":" "},{"type":"return-type","content":"Task"},{"type":"punctuation","content":""},{"type":"plain","content":" "},{"type":"function","content":"IsAccessTokenExpired"},{"type":"punctuation","content":"("},{"type":"class-name","content":"TokenValidatedContext"},{"type":"plain","content":" context"},{"type":"punctuation","content":","},{"type":"plain","content":" "},{"type":"class-name","content":"IServiceCollection"},{"type":"plain","content":" services"},{"type":"punctuation","content":")"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"{"}],[{"type":"plain","content":" "},{"type":"keyword","content":"try"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"{"}],[{"type":"plain","content":" "},{"type":"keyword","content":"var"},{"type":"plain","content":" requestHeader "},{"type":"operator","content":"="},{"type":"plain","content":" context"},{"type":"punctuation","content":"."},{"type":"plain","content":"Request"},{"type":"punctuation","content":"."},{"type":"plain","content":"Headers"},{"type":"punctuation","content":"["},{"type":"string","content":"\"Authorization\""},{"type":"punctuation","content":"]"},{"type":"punctuation","content":";"}],[{"type":"plain","content":" "},{"type":"keyword","content":"var"},{"type":"plain","content":" accessToken "},{"type":"operator","content":"="},{"type":"plain","content":" requestHeader"},{"type":"punctuation","content":"."},{"type":"plain","content":"Count "},{"type":"operator","content":">"},{"type":"plain","content":" "},{"type":"number","content":"0"},{"type":"plain","content":" "},{"type":"punctuation","content":"?"},{"type":"plain","content":" requestHeader"},{"type":"punctuation","content":"["},{"type":"number","content":"0"},{"type":"punctuation","content":"]"},{"type":"punctuation","content":"."},{"type":"function","content":"Split"},{"type":"punctuation","content":"("},{"type":"string","content":"\" \""},{"type":"punctuation","content":")"},{"type":"punctuation","content":"["},{"type":"number","content":"1"},{"type":"punctuation","content":"]"},{"type":"plain","content":" "},{"type":"punctuation","content":":"},{"type":"plain","content":" String"},{"type":"punctuation","content":"."},{"type":"plain","content":"Empty"},{"type":"punctuation","content":";"}],[{"type":"plain","content":" "},{"type":"keyword","content":"var"},{"type":"plain","content":" redisService "},{"type":"operator","content":"="},{"type":"plain","content":" context"},{"type":"punctuation","content":"."},{"type":"plain","content":"HttpContext"},{"type":"punctuation","content":"."},{"type":"plain","content":"RequestServices"},{"type":"punctuation","content":"."},{"type":"function","content":"GetRequiredService"},{"type":"punctuation","content":""},{"type":"punctuation","content":"("},{"type":"punctuation","content":")"},{"type":"punctuation","content":";"},{"type":"comment","content":"//GetRedisService(services);"}],[{"type":"plain","content":" "},{"type":"keyword","content":"var"},{"type":"plain","content":" blackToken "},{"type":"operator","content":"="},{"type":"plain","content":" "},{"type":"keyword","content":"await"},{"type":"plain","content":" redisService"},{"type":"punctuation","content":"."},{"type":"function","content":"GetCache"},{"type":"punctuation","content":"("},{"type":"plain","content":"TokenHelper"},{"type":"punctuation","content":"."},{"type":"function","content":"GetRedisKeyForBlackAccessToken"},{"type":"punctuation","content":"("},{"type":"plain","content":"accessToken"},{"type":"punctuation","content":")"},{"type":"punctuation","content":")"},{"type":"punctuation","content":";"}],[{"type":"plain","content":" "},{"type":"keyword","content":"return"},{"type":"plain","content":" blackToken "},{"type":"operator","content":"=="},{"type":"plain","content":" accessToken"},{"type":"punctuation","content":";"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"}"}],[{"type":"plain","content":" "},{"type":"keyword","content":"catch"},{"type":"plain","content":" "},{"type":"punctuation","content":"("},{"type":"class-name","content":"Exception"},{"type":"plain","content":" ex"},{"type":"punctuation","content":")"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"{"}],[{"type":"plain","content":" Log"},{"type":"punctuation","content":"."},{"type":"function","content":"Error"},{"type":"punctuation","content":"("},{"type":"plain","content":"ex"},{"type":"punctuation","content":","},{"type":"plain","content":" "},{"type":"string","content":"$\"Failed to validate access token: "},{"type":"punctuation","content":"{"},{"type":"expression","content":"ex"},{"type":"punctuation","content":"."},{"type":"expression","content":"Message"},{"type":"punctuation","content":"}"},{"type":"string","content":"\""},{"type":"punctuation","content":")"},{"type":"punctuation","content":";"}],[{"type":"plain","content":" "},{"type":"keyword","content":"return"},{"type":"plain","content":" "},{"type":"boolean","content":"true"},{"type":"punctuation","content":";"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"}"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"}"}]]}},{"type":"block","id":"aAHj-1739256962099","name":"paragraph","data":{},"nodes":[{"type":"text","id":"s4My-1739256962097","leaves":[{"text":"","marks":[]}]}],"state":{}},{"type":"block","id":"fQFS-1739351956632","name":"code","data":{"version":1,"language":"csharp","theme":"default"},"nodes":[{"type":"block","id":"ALaA-1739351956607","name":"code-line","data":{},"nodes":[{"type":"text","id":"QwSD-1739351956606","leaves":[{"text":"public class TokenHelper","marks":[]}]}]},{"type":"block","id":"0UiI-1739351956609","name":"code-line","data":{},"nodes":[{"type":"text","id":"IJmf-1739351956608","leaves":[{"text":"{","marks":[]}]}]},{"type":"block","id":"B3g8-1739351956611","name":"code-line","data":{},"nodes":[{"type":"text","id":"n0y9-1739351956610","leaves":[{"text":" public static string GetRedisKeyForBlackAccessToken(string userToken)","marks":[]}]}]},{"type":"block","id":"9vol-1739351956613","name":"code-line","data":{},"nodes":[{"type":"text","id":"SUKY-1739351956612","leaves":[{"text":" {","marks":[]}]}]},{"type":"block","id":"h5K8-1739351956615","name":"code-line","data":{},"nodes":[{"type":"text","id":"B6Hf-1739351956614","leaves":[{"text":" var handler = new JwtSecurityTokenHandler();","marks":[]}]}]},{"type":"block","id":"KwYN-1739351956617","name":"code-line","data":{},"nodes":[{"type":"text","id":"9TzJ-1739351956616","leaves":[{"text":" var jsonToken = handler.ReadJwtToken(userToken);","marks":[]}]}]},{"type":"block","id":"xZVG-1739351956619","name":"code-line","data":{},"nodes":[{"type":"text","id":"wwDZ-1739351956618","leaves":[{"text":" var dataJson = jsonToken.Payload.SerializeToJson();","marks":[]}]}]},{"type":"block","id":"BAJV-1739351956621","name":"code-line","data":{},"nodes":[{"type":"text","id":"u01p-1739351956620","leaves":[{"text":" JObject o = JObject.Parse(dataJson);","marks":[]}]}]},{"type":"block","id":"984A-1739351956623","name":"code-line","data":{},"nodes":[{"type":"text","id":"LQmu-1739351956622","leaves":[{"text":" var uuid = (string)o[\"sub\"];","marks":[]}]}]},{"type":"block","id":"J6tj-1739351956625","name":"code-line","data":{},"nodes":[{"type":"text","id":"jPlU-1739351956624","leaves":[{"text":" var signatureHashCode = jsonToken.RawSignature.GenerateHashString();","marks":[]}]}]},{"type":"block","id":"zksM-1739351956627","name":"code-line","data":{},"nodes":[{"type":"text","id":"0quR-1739351956626","leaves":[{"text":" return $\"BlackAccessToken_{uuid}_{signatureHashCode}\";","marks":[]}]}]},{"type":"block","id":"7mjD-1739351956629","name":"code-line","data":{},"nodes":[{"type":"text","id":"Mff7-1739351956628","leaves":[{"text":" }","marks":[]}]}]},{"type":"block","id":"m1VK-1739351956631","name":"code-line","data":{},"nodes":[{"type":"text","id":"YIQb-1739351956630","leaves":[{"text":"}","marks":[]}]}]},{"type":"block","id":"gja3-1739351974270","name":"code-line","data":{},"nodes":[{"type":"text","id":" vyO-1739351974269","leaves":[{"text":"","marks":[]}]}]},{"type":"block","id":"BI5l-1739351974463","name":"code-line","data":{},"nodes":[{"type":"text","id":"0RVv-1739351974462","leaves":[{"text":" public static string GenerateHashString(this string input)\r","marks":[]}]}]},{"type":"block","id":"bUNT-1739351974910","name":"code-line","data":{},"nodes":[{"type":"text","id":"N2Oz-1739351974909","leaves":[{"text":" {\r","marks":[]}]}]},{"type":"block","id":"mEwW-1739351974912","name":"code-line","data":{},"nodes":[{"type":"text","id":"ia5M-1739351974911","leaves":[{"text":" using MD5 md5 = MD5.Create();\r","marks":[]}]}]},{"type":"block","id":"abyg-1739351974914","name":"code-line","data":{},"nodes":[{"type":"text","id":" M5u-1739351974913","leaves":[{"text":" byte[] hash = md5.ComputeHash(Encoding.Default.GetBytes(input));\r","marks":[]}]}]},{"type":"block","id":"hWox-1739351974916","name":"code-line","data":{},"nodes":[{"type":"text","id":"5aXH-1739351974915","leaves":[{"text":" Guid result = new Guid(hash);\r","marks":[]}]}]},{"type":"block","id":"UEOh-1739351974918","name":"code-line","data":{},"nodes":[{"type":"text","id":"8rVw-1739351974917","leaves":[{"text":" return result.ToString();\r","marks":[]}]}]},{"type":"block","id":"sIyi-1739351974920","name":"code-line","data":{},"nodes":[{"type":"text","id":"XJ9A-1739351974919","leaves":[{"text":" }","marks":[]}]}]}],"state":{"tokensPerLine":[[{"type":"keyword","content":"public"},{"type":"plain","content":" "},{"type":"keyword","content":"class"},{"type":"plain","content":" "},{"type":"class-name","content":"TokenHelper"}],[{"type":"punctuation","content":"{"}],[{"type":"plain","content":" "},{"type":"keyword","content":"public"},{"type":"plain","content":" "},{"type":"keyword","content":"static"},{"type":"plain","content":" "},{"type":"keyword","content":"string"},{"type":"plain","content":" "},{"type":"function","content":"GetRedisKeyForBlackAccessToken"},{"type":"punctuation","content":"("},{"type":"keyword","content":"string"},{"type":"plain","content":" userToken"},{"type":"punctuation","content":")"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"{"}],[{"type":"plain","content":" "},{"type":"keyword","content":"var"},{"type":"plain","content":" handler "},{"type":"operator","content":"="},{"type":"plain","content":" "},{"type":"keyword","content":"new"},{"type":"plain","content":" "},{"type":"constructor-invocation","content":"JwtSecurityTokenHandler"},{"type":"punctuation","content":"("},{"type":"punctuation","content":")"},{"type":"punctuation","content":";"}],[{"type":"plain","content":" "},{"type":"keyword","content":"var"},{"type":"plain","content":" jsonToken "},{"type":"operator","content":"="},{"type":"plain","content":" handler"},{"type":"punctuation","content":"."},{"type":"function","content":"ReadJwtToken"},{"type":"punctuation","content":"("},{"type":"plain","content":"userToken"},{"type":"punctuation","content":")"},{"type":"punctuation","content":";"}],[{"type":"plain","content":" "},{"type":"keyword","content":"var"},{"type":"plain","content":" dataJson "},{"type":"operator","content":"="},{"type":"plain","content":" jsonToken"},{"type":"punctuation","content":"."},{"type":"plain","content":" ayload"},{"type":"punctuation","content":"."},{"type":"function","content":"SerializeToJson"},{"type":"punctuation","content":"("},{"type":"punctuation","content":")"},{"type":"punctuation","content":";"}],[{"type":"plain","content":" "},{"type":"class-name","content":"JObject"},{"type":"plain","content":" o "},{"type":"operator","content":"="},{"type":"plain","content":" JObject"},{"type":"punctuation","content":"."},{"type":"function","content":" arse"},{"type":"punctuation","content":"("},{"type":"plain","content":"dataJson"},{"type":"punctuation","content":")"},{"type":"punctuation","content":";"}],[{"type":"plain","content":" "},{"type":"keyword","content":"var"},{"type":"plain","content":" uuid "},{"type":"operator","content":"="},{"type":"plain","content":" "},{"type":"punctuation","content":"("},{"type":"keyword","content":"string"},{"type":"punctuation","content":")"},{"type":"plain","content":"o"},{"type":"punctuation","content":"["},{"type":"string","content":"\"sub\""},{"type":"punctuation","content":"]"},{"type":"punctuation","content":";"}],[{"type":"plain","content":" "},{"type":"keyword","content":"var"},{"type":"plain","content":" signatureHashCode "},{"type":"operator","content":"="},{"type":"plain","content":" jsonToken"},{"type":"punctuation","content":"."},{"type":"plain","content":"RawSignature"},{"type":"punctuation","content":"."},{"type":"function","content":"GenerateHashString"},{"type":"punctuation","content":"("},{"type":"punctuation","content":")"},{"type":"punctuation","content":";"}],[{"type":"plain","content":" "},{"type":"keyword","content":"return"},{"type":"plain","content":" "},{"type":"string","content":"$\"BlackAccessToken_"},{"type":"punctuation","content":"{"},{"type":"expression","content":"uuid"},{"type":"punctuation","content":"}"},{"type":"string","content":"_"},{"type":"punctuation","content":"{"},{"type":"expression","content":"signatureHashCode"},{"type":"punctuation","content":"}"},{"type":"string","content":"\""},{"type":"punctuation","content":";"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"}"}],[{"type":"punctuation","content":"}"}],[],[{"type":"plain","content":" "},{"type":"keyword","content":"public"},{"type":"plain","content":" "},{"type":"keyword","content":"static"},{"type":"plain","content":" "},{"type":"keyword","content":"string"},{"type":"plain","content":" "},{"type":"function","content":"GenerateHashString"},{"type":"punctuation","content":"("},{"type":"keyword","content":"this"},{"type":"plain","content":" "},{"type":"keyword","content":"string"},{"type":"plain","content":" input"},{"type":"punctuation","content":")"},{"type":"plain","content":"\r"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"{"},{"type":"plain","content":"\r"}],[{"type":"plain","content":" "},{"type":"keyword","content":"using"},{"type":"plain","content":" "},{"type":"class-name","content":"MD5"},{"type":"plain","content":" md5 "},{"type":"operator","content":"="},{"type":"plain","content":" MD5"},{"type":"punctuation","content":"."},{"type":"function","content":"Create"},{"type":"punctuation","content":"("},{"type":"punctuation","content":")"},{"type":"punctuation","content":";"},{"type":"plain","content":"\r"}],[{"type":"plain","content":" "},{"type":"keyword","content":"byte"},{"type":"punctuation","content":"["},{"type":"punctuation","content":"]"},{"type":"plain","content":" hash "},{"type":"operator","content":"="},{"type":"plain","content":" md5"},{"type":"punctuation","content":"."},{"type":"function","content":"ComputeHash"},{"type":"punctuation","content":"("},{"type":"plain","content":"Encoding"},{"type":"punctuation","content":"."},{"type":"plain","content":"Default"},{"type":"punctuation","content":"."},{"type":"function","content":"GetBytes"},{"type":"punctuation","content":"("},{"type":"plain","content":"input"},{"type":"punctuation","content":")"},{"type":"punctuation","content":")"},{"type":"punctuation","content":";"},{"type":"plain","content":"\r"}],[{"type":"plain","content":" "},{"type":"class-name","content":"Guid"},{"type":"plain","content":" result "},{"type":"operator","content":"="},{"type":"plain","content":" "},{"type":"keyword","content":"new"},{"type":"plain","content":" "},{"type":"constructor-invocation","content":"Guid"},{"type":"punctuation","content":"("},{"type":"plain","content":"hash"},{"type":"punctuation","content":")"},{"type":"punctuation","content":";"},{"type":"plain","content":"\r"}],[{"type":"plain","content":" "},{"type":"keyword","content":"return"},{"type":"plain","content":" result"},{"type":"punctuation","content":"."},{"type":"function","content":"ToString"},{"type":"punctuation","content":"("},{"type":"punctuation","content":")"},{"type":"punctuation","content":";"},{"type":"plain","content":"\r"}],[{"type":"plain","content":" "},{"type":"punctuation","content":"}"}]]}},{"type":"block","id":"RU9P-1739256962435","name":"paragraph","data":{},"nodes":[{"type":"text","id":"fAsK-1739256962433","leaves":[{"text":"","marks":[]}]}],"state":{}},{"type":"block","id":"H6Vk-1739351961959","name":"paragraph","data":{},"nodes":[{"type":"text","id":"Y4QA-1739351961958","leaves":[{"text":"","marks":[]}]}],"state":{}}]">前文说过 token 由 3 个部门组成:分别是 token metadata,payload,signature,其中 signature 部门是对 payload 的加密,而 payload 当中会包含 token 的过期时间。 这样做有个利益:服务端不需要别的去存储这个 session 的状态,只要校验过 signature 有效,就可以拿到过期时间,简化后端逻辑,做到 “无状态化”。但这样做也会有一个缺点,只要 token 签发了,就无法更改这个 token 的内容。 题目场景
一个用户登录,得到一个有效的 token 之后,他点击退出登录。此时理想状态下,我们盼望这个 token 不再生效,但实际是,只要拿着这个 token 去访问服务端 Authenticated 的资源,token 仍然会校验通过。因为 [退出登录] 的操作本身不能改变 token。 解决方案
一、把 token 的有效期缩短,例如半小时或者五分钟,但有个明显的缺陷:用户需要频仍重新登录。
二、把 退出登录 的用户添加到 token black list 当中。
简单地,在调 sign out (退出登录) 的 api 时,把用户的 access token 添加到 token black list 当中;后端校验 jwt 时,添加校验 token 是否存在于 token black list 当中。下面展开设计过程: 1. token black list 的设计
是否长期化?
第一个题目是:这种 sign out 的 token 要不要长期化?首先,token 本身就是会过期的;其次,这个新的校验方法会作用到每一个通过了 token 有效验证的哀求,这个方法肯定是高频访问的;所以,此处选择通过 redis 缓存 tokenBlackList 。(Redis 是内存数据库,支持高并发读写和主动过期(TTL),适合存储临时性黑名单数据。纵然服务重启,黑名单数据可能丢失,但 Token 本身有过期时间,因此不影响最终一致性。)每次 sign out,都将 set 到 redis 中;每次校验 token,都查询这个 redis value。 数据结构设计
REDIS 是 key,value 的键值对方式,value 可能是 string,list,hash..对于 value,可以直接粗暴的存储整个 token json;那么 key 应该如何设计?使用 userId,那大概率会和其他业务的 redis key 重叠,在这里最好加上业务场景,形如,“TOKEN_BLACK_LIST_userId”。 a. 多设备登录场景
假设:用户 A 在设备 D1 上登录后,在设备 D2 上同时登岸(这种场景当前是允许的);此时用户 A 在设备 D1 上点击 退出登录,服务端会把 TOKEN_BLACK_LIST_AId : tokenJson 写进了redis。此时用户 A 再于设备 D2 上操作,校验 token 时会去 redis 捞取数据,找到了 TOKEN_BLACK_LIST_AId,此时认为用户 A 的 token 无效。 假如这不是我们盼望的场景,那应该如何让同个账号的多个 token 互不影响呢?此处 userId 就不适合作为 redis key。是否每个 token 有本身特有的唯一的 id 呢?这又到 token payload 的组成,它确实存在唯一标识的 id,jti- {
- "jti": "a1b2c3d4-5678-90ef-ghij-klmnopqrstuv",
- "iss": "the issuer",
- "aud": "the audience",
- "exp": 1630003600,
- "iat": 1630000000,
- ..
- }
复制代码 此故,这里把 key 设计成 [TOKEN_BLACK_LIST_tokenId],形如 "TOKEN_BLACK_LIST_a1b2c3d4-5678-90ef-ghij-klmnopqrstuv"。 b. 修改暗码场景
假设:用户 A 在设备 D1、D2、D3 .. 多设备上均操作登录,此时每个设备都持有一个独立的 token;假如此时用户在设备 D1 上 “修改暗码”,如何让 D2、D3 等全部设备的登录失效? 后端可以把提出 “修改暗码” 的设备 D1 的 token 加入到 token_black_list 当中,但是如何知道这个用户当前持有多少 token 呢?是否需要每次登录都把 token 存储起来?但这样显然会增加复杂度。 我们可以重新审视一下 token 的结构,是否能找到一些属性来使用?- {
- "jti": "a1b2c3d4-5678-90ef-ghij-klmnopqrstuv",
- "iss": "the issuer",
- "aud": "the audience",
- "exp": 1630003600,
- "iat": 1630000000,
- ..
- }
复制代码 这里有一个非常奇妙而简单的方式:每一个 token 上都会持有 iat 签发时间,假设 用户 A 在设备 D1 上确定 “修改暗码” 的时间是 changedPasswordDate,服务端处理完 “修改暗码” 之后,可以把 changedPasswordDate 这个时间存储到 redis 上,设其 key 为 TOKEN_INVALIDATION_userId,value 为 changedPasswordDate; 那么在服务端校验 token 需要多添加这两项校验: 查询当前 token 是否存在于 TOKEN_BLACK_LIST 中 查询是否存在 TOKEN_INVALIDATION_userId,假如存在, 比较当前 token 的 iat 时间是否早于 changedPasswordDate,假如是,该 token 无效 2. code implement
sign-out / change-password
redis key-value 的过期时间取 token 的有效周期。本文设定 token 有效期为24小时,也即 1440 分钟。- public async Task<GlobalSignOutResponse> SignOutAsync(string accessToken)
- {
- var response = await _authService.SignOutAsync(accessToken);
- await _redisCacheService.SetCache(TokenHelper.GetRedisKeyForBlackAccessToken(accessToken), accessToken, 1440); // 分钟单位
- return response;
- }
复制代码 "修改暗码" 的处理同理。 jwt authentication
startUp.cs- services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
- .AddJwtBearer(options =>
- {
- ..
- options.Events = new JwtBearerEvents
- {
- ..
- OnTokenValidated = async context =>
- {
- if (await IsAccessTokenExpired(context, services))
- {
- Log.Information($"The access token is expired as user already signed out or changed password.");
- context.Fail(GetTokenExpiredResponse(context.Response));
- }
- await Task.CompletedTask;
- }
- };
- });
复制代码 - private string GetTokenExpiredResponse(HttpResponse response)
- {
- if (ApiResponseCodes.AccessTokenExpired.BuildHttpResponse() is ObjectResult result)
- {
- var payload = JObject.FromObject(result.Value);
- response.ContentType = "application/json";
- response.StatusCode = 401;
- return payload.ToString();
- }
- return string.Empty;
- }
- private async Task<bool> IsAccessTokenExpired(TokenValidatedContext context, IServiceCollection services)
- {
- try
- {
- var requestHeader = context.Request.Headers["Authorization"];
- var accessToken = requestHeader.Count > 0 ? requestHeader[0].Split(" ")[1] : String.Empty;
- var redisService = context.HttpContext.RequestServices.GetRequiredService<IRedisCacheService>()
- var blackToken = await redisService.GetCache(TokenHelper.GetRedisKeyForBlackAccessToken(accessToken));
- return blackToken == accessToken;
- }
- catch (Exception ex)
- {
- Log.Error(ex, $"Failed to validate access token: {ex.Message}");
- return true;
- }
- }
复制代码 * 由 [退出登录] 无效化 token,还可以衍生出非常多的题目,此处暂且不表。 ..
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |