1、前言

            這塊兒當(dāng)時在IdentityServer4和JWT之間猶豫了一下,后來考慮到現(xiàn)狀,出于3個原因,暫時放棄了IdentityServer4選擇了JWT:

          (1)目前這個前端框架更適配JWT;

          (2)前后端分離的項目,如果上IdentityServer4,還要折騰點兒工作,比如前端配置、多余的回調(diào)等;

          (3)跨度太大,團(tuán)隊、系統(tǒng)、歷史數(shù)據(jù)接入都是問題,解決是可以解決,但時間有限,留待后續(xù)吧;

            當(dāng)然,只是暫時放棄,理想中的最佳實踐還是IdentityServer4做統(tǒng)一鑒權(quán)的。

          2、JWT認(rèn)證實現(xiàn)

          (1)Common項目下定義JWTConfig配置對象



          ?

          ?(2)系統(tǒng)配置文件中增加JWT參數(shù)配置



          ?

          ?

          ?此處配置與(1)中的配置對象是對應(yīng)的。

          ?

          (3)JWT處理程序及相關(guān)服務(wù)注冊
          1 services.Configure<JWTConfig>(Configuration.GetSection("JWT")); 2 var
          jwtConfig = Configuration.GetSection("JWT").Get<JWTConfig>(); 3
          services.AddAuthentication(options => 4 { 5 options.DefaultAuthenticateScheme
          = JwtBearerDefaults.AuthenticationScheme; 6 options.DefaultChallengeScheme =
          JwtBearerDefaults.AuthenticationScheme; 7 }) 8 .AddJwtBearer(options => 9 {
          10 options.TokenValidationParameters = new TokenValidationParameters 11 { 12
          ValidateIssuer =true, 13 ValidIssuer = jwtConfig.Issuer, 14
          ValidateIssuerSigningKey =true, 15 IssuerSigningKey = new
          SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.SymmetricSecurityKey)),16
          ValidateAudience =false, 17 ValidateLifetime = true, 18 ClockSkew =
          TimeSpan.FromMinutes(5) 19 }; 20 options.Events = new JwtBearerEvents 21 { 22
          OnTokenValidated = context => 23 { 24 var userContext =
          context.HttpContext.RequestServices.GetService<UserContext>(); 25 var claims =
          context.Principal.Claims; 26 userContext.ID = long.Parse(claims.First(x =>
          x.Type == JwtRegisteredClaimNames.Sub).Value); 27 userContext.Account =
          claims.First(x => x.Type == ClaimTypes.NameIdentifier).Value; 28
          userContext.Name = claims.First(x => x.Type == ClaimTypes.Name).Value; 29
          userContext.Email = claims.First(x => x.Type ==
          JwtRegisteredClaimNames.Email).Value; 30 userContext.RoleId = claims.First(x =>
          x.Type == ClaimTypes.Role).Value; 31 32 return Task.CompletedTask; 33 }34 }; 35
          });36 JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
          ?


          ?  上述代碼中注意紅色那部分Token驗證成功的事件注冊,其目的是認(rèn)證成功之后,從JWT中取出必要信息構(gòu)建當(dāng)前用戶上下文,這個上下文信息非常重要,但凡涉及到需要獲取當(dāng)前用戶相關(guān)信息的部分,都要依賴它,后續(xù)文章中對應(yīng)部分還會提及。

          (4)登錄并寫入Token
          1 /// <summary> 2 /// 登錄 3 /// </summary> 4 /// <param
          name="userDto"></param> 5 /// <returns></returns> 6 [AllowAnonymous] 7
          [HttpPost("login")] 8 public async Task<IActionResult>
          Login([FromBody]SysUserDto userDto) 9 { 10 var validateResult = await
          _accountService.ValidateCredentials(userDto.Account, userDto.Password);11 if (!
          validateResult.Item1)12 { 13 return new NotFoundObjectResult("用戶名或密碼錯誤"); 14 }
          15 16 var user = validateResult.Item2; 17 var claims = new Claim[] 18 { 19 new
          Claim(ClaimTypes.NameIdentifier, user.Account),20 new Claim(ClaimTypes.Name,
          user.Name),21 new Claim(JwtRegisteredClaimNames.Email, user.Email), 22 new
          Claim(JwtRegisteredClaimNames.Sub, user.ID.ToString()),23 new
          Claim(ClaimTypes.Role, user.RoleId)24 }; 25 26 var key = new
          SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtConfig.SymmetricSecurityKey));27
          28 var token = new JwtSecurityToken( 29 issuer: _jwtConfig.Issuer, 30 audience:
          null, 31 claims: claims, 32 notBefore: DateTime.Now, 33 expires:
          DateTime.Now.AddHours(2), 34 signingCredentials: new SigningCredentials(key,
          SecurityAlgorithms.HmacSha256)35 ); 36 37 var jwtToken = new
          JwtSecurityTokenHandler().WriteToken(token);38 39 return new JsonResult(new {
          token = jwtToken }); 40 }
          ?(5)前端Token狀態(tài)保存


            一般來講,在后端登錄成功返回前端之后,前端需要保存此token以保持狀態(tài),否則一刷新全完蛋,那我們來看看前端怎么實現(xiàn)。由于前端項目引入了Vuex來保持狀態(tài),那api調(diào)用、狀態(tài)操作自然就放在store中,我們來看看登錄對應(yīng)的store。打開前端源碼,找到user這個store:



          ?

          ?

          ?  我們看到,登錄完畢,調(diào)用SET_TOKEN這個mutation提交token狀態(tài)變更保存Token,這個mutation及其背后的state如下如下:



          ?

          ?同時,在登錄action中,登錄成功之后,我們還發(fā)現(xiàn)了一行代碼:



          ?

          ?  此setToken引自前端工具類,auth.js,代碼如下:
          1 import Cookies from 'js-cookie' 2 3 const TokenKey = 'ngcc_mis_token' 4
          5 export function getToken() { 6 return Cookies.get(TokenKey) 7 } 8 9 export
          function setToken(token) { 10 return Cookies.set(TokenKey, token) 11 } 12 13
          exportfunction removeToken() { 14 return Cookies.remove(TokenKey) 15 }

            至此我們明白了前端的機(jī)制,把token寫入Vuex狀態(tài)的同時,再寫入cookie。那這里問一句,只寫入Vuex狀態(tài),行不行呢?不行,因為瀏覽器一刷新,所有前端對象就會銷毀,包括Vuex對象,這樣會導(dǎo)致前端丟失會話。同時,我們再擴(kuò)展深入下,假如這里我們要做點單登錄,不借助IdentityServer4這種統(tǒng)一認(rèn)證平臺的話,怎么做呢?其實很簡單,這里寫入cookie改為寫入localstoage這種瀏覽器中可以跨域共享的存儲就可以了。

          3、總結(jié)

            以上就是系統(tǒng)認(rèn)證的實現(xiàn),大家摸清楚各種認(rèn)證方案、優(yōu)缺點、特點,多深入源碼、機(jī)制,遇到問題自然會手到擒來。
          SET_TOKEN

          友情鏈接
          ioDraw流程圖
          API參考文檔
          OK工具箱
          云服務(wù)器優(yōu)惠
          阿里云優(yōu)惠券
          騰訊云優(yōu)惠券
          京東云優(yōu)惠券
          站點信息
          問題反饋
          郵箱:[email protected]
          QQ群:637538335
          關(guān)注微信

                91自啪区| 九色综合网 | 影音先锋成人站 | 看黄色视频的网站 | 大骚逼导航|