Core + Vue 後臺管理基礎框架2——認證

一、前言前端

  這塊兒當時在IdentityServer4和JWT之間猶豫了一下,後來考慮到現狀,出於3個緣由,暫時放棄了IdentityServer4選擇了JWT:後端

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

(2)先後端分離的項目,若是上IdentityServer4,還要折騰點兒工做,好比前端配置、多餘的回調等;跨域

(3)跨度太大,團隊、系統、歷史數據接入都是問題,解決是能夠解決,但時間有限,留待後續吧;瀏覽器

  固然,只是暫時放棄,理想中的最佳實踐仍是IdentityServer4作統一鑑權的。前端框架

二、JWT認證明現cookie

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

 

 (2)系統配置文件中增長JWT參數配置前後端分離

 

 

 此處配置與(1)中的配置對象是對應的。async

 

(3)JWT處理程序及相關服務註冊

 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驗證成功的事件註冊,其目的是認證成功以後,從JWT中取出必要信息構建當前用戶上下文,這個上下文信息很是重要,但凡涉及到須要獲取當前用戶相關信息的部分,都要依賴它,後續文章中對應部分還會說起。

(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狀態保存

  通常來說,在後端登陸成功返回前端以後,前端須要保存此token以保持狀態,不然一刷新全完蛋,那咱們來看看前端怎麼實現。因爲前端項目引入了Vuex來保持狀態,那api調用、狀態操做天然就放在store中,咱們來看看登陸對應的store。打開前端源碼,找到user這個store:

 

 

   咱們看到,登陸完畢,調用SET_TOKEN這個mutation提交token狀態變動保存Token,這個mutation及其背後的state以下以下:

 

 同時,在登陸action中,登陸成功以後,咱們還發現了一行代碼:

 

   此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 export function removeToken() {
14   return Cookies.remove(TokenKey)
15 }

  至此咱們明白了前端的機制,把token寫入Vuex狀態的同時,再寫入cookie。那這裏問一句,只寫入Vuex狀態,行不行呢?不行,由於瀏覽器一刷新,全部前端對象就會銷燬,包括Vuex對象,這樣會致使前端丟失會話。同時,咱們再擴展深刻下,假如這裏咱們要作點單登陸,不借助IdentityServer4這種統一認證平臺的話,怎麼作呢?其實很簡單,這裏寫入cookie改成寫入localstoage這種瀏覽器中能夠跨域共享的存儲就能夠了。

三、總結

  以上就是系統認證的實現,你們摸清楚各類認證方案、優缺點、特色,多深刻源碼、機制,遇到問題天然會手到擒來。

SET_TOKEN
相關文章
相關標籤/搜索