using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; namespace FrontendWebApi.Jwt { public interface IJwtHelpers { TnToken GenerateToken(JwtLogin login); } public class JwtHelpers: IJwtHelpers { private readonly IConfiguration _configuration; public JwtHelpers(IConfiguration configuration) { _configuration = configuration; } public TnToken GenerateToken(JwtLogin login) { var issuer = _configuration.GetValue("JwtSettings:Issuer"); var signKey = _configuration.GetValue("JwtSettings:SignKey"); var lifeseconds = _configuration.GetValue("JwtSettings:JwtLifeSeconds"); // 設定要加入到 JWT Token 中的聲明資訊(Claims) var claims = new List(); // 在 RFC 7519 規格中(Section#4),總共定義了 7 個預設的 Claims,我們應該只用的到兩種! claims.Add(new Claim(JwtRegisteredClaimNames.Iss, issuer)); //claims.Add(new Claim(JwtRegisteredClaimNames.NameId, login.CustomerNo.ToString())); //claims.Add(new Claim(JwtRegisteredClaimNames.Sub, login.Username)); // User.Identity.Name //claims.Add(new Claim(JwtRegisteredClaimNames.Aud, "The Audience")); claims.Add(new Claim(JwtRegisteredClaimNames.Exp, DateTimeOffset.UtcNow.AddSeconds(lifeseconds).ToUnixTimeSeconds().ToString())); claims.Add(new Claim(JwtRegisteredClaimNames.Nbf, DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString())); // 必須為數字 //claims.Add(new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString())); // 必須為數字 //claims.Add(new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())); // JWT ID // 網路上常看到的這個 NameId 設定是多餘的 //claims.Add(new Claim(JwtRegisteredClaimNames.NameId, userName)); // 這個 Claim 也以直接被 JwtRegisteredClaimNames.Sub 取代,所以也是多餘的 //claims.Add(new Claim(ClaimTypes.Name, userName)); // 你可以自行擴充 "roles" 加入登入者該有的角色 //claims.Add(new Claim("roles", "Users")); //claims.Add(new Claim("groupid", login.AreaCode)); claims.Add(new Claim("userinfo_guid", login.userinfo_guid)); claims.Add(new Claim("account", login.account)); claims.Add(new Claim("full_name", login.full_name)); claims.Add(new Claim("email", login.email)); var userClaimsIdentity = new ClaimsIdentity(claims); // 建立一組對稱式加密的金鑰,主要用於 JWT 簽章之用 var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(signKey)); // HmacSha256 有要求必須要大於 128 bits,所以 key 不能太短,至少要 16 字元以上 // https://stackoverflow.com/questions/47279947/idx10603-the-algorithm-hs256-requires-the-securitykey-keysize-to-be-greater var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature); var now = DateTime.UtcNow; var expires = DateTime.UtcNow.AddSeconds(lifeseconds); // 產出所需要的 JWT securityToken 物件,並取得序列化後的 Token 結果(字串格式) var tokenHandler = new JwtSecurityTokenHandler(); var jst = new JwtSecurityToken( issuer: issuer,//Token釋出者 //audience: _options.Value.Audience,//Token接受者 claims: claims,//攜帶的負載 notBefore: now,//當前時間token生成時間 expires: expires,//過期時間 signingCredentials: signingCredentials ); //var jst = new SecurityTokenDescriptor //{ // Issuer = issuer,//Token釋出者 // //Claims = claims,//攜帶的負載 // //audience: _options.Value.Audience,//Token接受者 // NotBefore = now,//當前時間token生成時間 // Expires = expires,//過期時間 // SigningCredentials = signingCredentials, // Subject = userClaimsIdentity //}; var serializeToken = tokenHandler.WriteToken(jst); var data = new TnToken(); data.token = serializeToken; data.type = "bearer"; data.expires= lifeseconds; return data; } } }