publicstaticstringGenerateToken(ClaimsIdentity identity, int expireMinutes = 20) { var symmetricKey = Convert.FromBase64String(_secret); var tokenHandler = new JwtSecurityTokenHandler(); var now = DateTime.UtcNow;
var tokenDescriptor = new SecurityTokenDescriptor { Subject = identity, Expires = now.AddMinutes(expireMinutes), SigningCredentials = new SigningCredentials( new SymmetricSecurityKey(symmetricKey), SecurityAlgorithms.HmacSha256Signature ) };
var stoken = tokenHandler.CreateToken(tokenDescriptor); var token = tokenHandler.WriteToken(stoken); return token; }
publicstaticboolTryValidateToken(string token, out ClaimsPrincipal principal) { principal = null; try { var tokenHandler = new JwtSecurityTokenHandler(); var jwtToken = tokenHandler.ReadToken(token); if (jwtToken == null) returnfalse;
var symmetricKey = Convert.FromBase64String(_secret);
var validationParameters = new TokenValidationParameters() { RequireExpirationTime = true, ValidateIssuer = false, ValidateAudience = false, IssuerSigningKey = new SymmetricSecurityKey(symmetricKey) };
SecurityToken securityToken; principal = tokenHandler.ValidateToken(token, validationParameters, out securityToken);
protected ISystemAuthorityModule SystemAuthorityModule { get => this._systemAuthorityModule ?? (this._systemAuthorityModule = ModuleFactory.GetSystemAuthorityModule()); set => this._systemAuthorityModule = value; }
protectedoverrideboolAuthorizeCore(HttpContextBase httpContext) { // 解析header取得principal var principal = ParseAuthorizeHeader(httpContext); if (principal == null) returnfalse;
// 取得員工編號 var employeeNo = int.Parse(principal.FindFirst(ClaimTypes.NameIdentifier).Value); if (employeeNo == 0) returnfalse;
// 驗證權限 var controller = Convert.ToString(httpContext.Request.RequestContext.RouteData.Values["controller"]); var action = Convert.ToString(httpContext.Request.RequestContext.RouteData.Values["action"]); var urls = new[] { $"/{controller}", $"/{controller}/{action}" }; var result = this.SystemAuthorityModule.ValidateUserFunction(employeeNo, urls);
return result; }
private ClaimsPrincipal ParseAuthorizeHeader(HttpContextBase httpContext) { var token = httpContext.Request.Headers["Authorization"]; if (string.IsNullOrEmpty(token)) returnnull;
token = token.Replace("Bearer ", string.Empty); var principal = JWTHelper.GetPrincipal(token);
// 驗證使用者 var user = UserModule.VerifyUser(loginForm.Account, loginForm.Password); if (user == null) { return Json(new APIResult() { Message = "帳號密碼錯誤", IsSuccess = false }); }
// 產生token var identity = new ClaimsIdentity( new[] { new Claim(ClaimTypes.NameIdentifier, user.EmployeeNo.ToString()), new Claim(ClaimTypes.Name, user.UserName), new Claim("Account", user.Account), new Claim(ClaimTypes.Email, user.EmailAccount), }); var token = JWTHelper.GenerateToken(identity);
另外的方式當然就是在跑測試的時候將網站先手動啟動,然後再去執行測試,這樣的方式僅適合用來開發時期驗證一下,以現在專案大多都與 CI Server 整合在一起的情況,應該是不太能夠在 CI Server 上還在每次 Build 都先手動掛載網站;不過測試程式還有一個很棒的意義,就是提供人家理解,怎麼去使用你所開發的程式,相當於使用說明書了,如果你的程式介面沒有高竿到讓人一看簽章方法就能用,還是提供一下說明書比較好些
[TestMethod()] publicvoidGenerateToken() { var user = GetTestUser(); var identity = new ClaimsIdentity( new [] { new Claim(ClaimTypes.NameIdentifier, user.EmployeeNo.ToString()), new Claim(ClaimTypes.Name, user.UserName), new Claim("Account",user.UserName), new Claim(ClaimTypes.Email, user.EmailAccount), }); var token = JWTHelper.GenerateToken(identity); Console.WriteLine(token); } private User GetTestUser() { returnnew User() { Account = "spiderman", EmailAccount = "spiderman@email.com", UserName = "蜘蛛人", EmployeeNo = 123456 }; }
[TestMethod] publicvoidParseToken() { var expected = GetTestUser(); var token = GenerateTestToken(); var principal = JWTHelper.GetPrincipal(token); Assert.AreEqual(expected.UserName, principal.Identity.Name); Assert.AreEqual(expected.EmployeeNo.ToString(), principal.FindFirst(ClaimTypes.NameIdentifier).Value); Assert.AreEqual(expected.Account, principal.FindFirst("Account").Value); Assert.AreEqual(expected.EmailAccount, principal.FindFirst(ClaimTypes.Email).Value); }
privatestringGenerateTestToken() { var user = GetTestUser(); var identity = new ClaimsIdentity( new[] { new Claim(ClaimTypes.NameIdentifier, user.EmployeeNo.ToString()), new Claim(ClaimTypes.Name, user.UserName), new Claim("Account", user.Account), new Claim(ClaimTypes.Email, user.EmailAccount), }); var token = JWTHelper.GenerateToken(identity); return token; }
[TestInitialize] publicvoidBeforeEach() { _client = new HttpClient { BaseAddress = new Uri(Host) }; }
[TestMethod] publicvoid 無token執行回應unauthorized() { var queryUrl = "JWT/FeatureB";
var queryResponse = _client.PostAsync(queryUrl, null).Result; Assert.AreEqual(HttpStatusCode.Unauthorized, queryResponse.StatusCode); }
[TestMethod] publicvoid 有token無權限執行回應unauthorized() { var queryUrl = "JWT/FeatureB"; var token = GenerateToken(); _client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}"); var queryResponse = _client.PostAsync(queryUrl, null).Result; Assert.AreEqual(HttpStatusCode.Unauthorized, queryResponse.StatusCode); }
[TestMethod] publicvoid 有權限執行回應ok() { var queryUrl = "JWT/FeatureA"; var token = GenerateToken(); _client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}"); var queryResponse = _client.PostAsync(queryUrl, null).Result; Assert.AreEqual(HttpStatusCode.OK, queryResponse.StatusCode); }
privatestringGenerateToken() { var identity = new ClaimsIdentity( new[] { new Claim(ClaimTypes.NameIdentifier, "123456"), new Claim(ClaimTypes.Name, "蜘蛛人"), new Claim("Account", "spiderman"), new Claim(ClaimTypes.Email, "spiderman@email.com"), }); var token = JWTHelper.GenerateToken(identity); return token; } }
第一篇文章中的Token signed with key A -> Token verified with key B (RSA scenario)這個驗證,我嘗試了一個晚上還是做不出來 POC,我想是因為相關知識太薄弱了,時間有限,我決定放棄這件事情,改為研究第二篇文章所說的,自訂編碼方式將 token 的 payload 加密