JwtAuthExtension.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. using Microsoft.AspNetCore.Authentication.JwtBearer;
  2. using Microsoft.AspNetCore.Authorization;
  3. using Microsoft.Extensions.Configuration;
  4. using Microsoft.Extensions.DependencyInjection;
  5. using Microsoft.IdentityModel.Tokens;
  6. using System;
  7. using System.Threading.Tasks;
  8. using System.Security.Claims;
  9. using System.IdentityModel.Tokens.Jwt;
  10. using System.Collections.Generic;
  11. using System.Text;
  12. using System.ComponentModel;
  13. namespace TEAMModelOS.SDK.Extension
  14. {
  15. public static class JwtAuthExtension
  16. {
  17. public static string CreateAuthToken(string issuer, string id, string name, string picture, string salt, string scope, string Website, string areaId = "", string schoolID = "", string standard = "", string[] roles = null, string[] permissions = null, int expire = 1)
  18. {
  19. // 設定要加入到 JWT Token 中的聲明資訊(Claims)
  20. var payload = new JwtPayload {
  21. { JwtRegisteredClaimNames.Iss, issuer }, //發行者
  22. { JwtRegisteredClaimNames.Sub, id }, // 用戶ID
  23. { JwtRegisteredClaimNames.Azp,schoolID}, // 學校簡碼,如果有的話
  24. { JwtRegisteredClaimNames.Exp,DateTimeOffset.UtcNow.AddHours(expire).ToUnixTimeSeconds()}, // 到期的時間,必須為數字
  25. { "name",name}, // 用戶的顯示名稱
  26. { "picture",picture}, // 用戶頭像
  27. { "roles",roles}, // 登入者的角色,角色類型 (Admin、Teacher、Student)
  28. { "permissions",permissions}, //登入者的權限請求
  29. { "standard",standard} ,//登入者的能力点标准
  30. { "scope",scope}, //登入者的入口类型。 (teacher 教师端登录的醍摩豆ID、tmduser学生端登录的醍摩豆ID、student学生端登录校内账号的学生ID)
  31. { "area",areaId==null?"":areaId},
  32. { JwtRegisteredClaimNames.Website,Website},
  33. };
  34. // 建立一組對稱式加密的金鑰,主要用於 JWT 簽章之用
  35. var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(salt));
  36. // HmacSha256 有要求必須要大於 128 bits,所以 salt 不能太短,至少要 16 字元以上
  37. // https://stackoverflow.com/questions/47279947/idx10603-the-algorithm-hs256-requires-the-securitykey-keysize-to-be-greater
  38. var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
  39. var header = new JwtHeader(signingCredentials);
  40. var secToken = new JwtSecurityToken(header, payload);
  41. // 產出所需要的 JWT securityToken 物件,並取得序列化後的 Token 結果(字串格式)
  42. var tokenHandler = new JwtSecurityTokenHandler();
  43. //var securityToken = tokenHandler.CreateToken(tokenDescriptor);
  44. var serializeToken = tokenHandler.WriteToken(secToken);
  45. return serializeToken;
  46. }
  47. /// <summary>
  48. ///
  49. /// </summary>
  50. /// <param name="issuer">颁发者</param>
  51. /// <param name="id">第三方合作uuid</param>
  52. /// <param name="salt"></param>
  53. /// <param name="expire"></param>
  54. /// <returns></returns>
  55. public static (string jwt , string jti) CreateBusinessApiToken(string location, string id, string salt )
  56. {
  57. string scope = "business";
  58. var keys = OpenApiJtwIssuer.OpenApiJtw签发者.GetDescriptionText().Split(',');
  59. string issuer = "";
  60. if (location.Equals("China-Dep"))
  61. {
  62. issuer = keys[0];
  63. }
  64. else if (location.Equals("China-Test"))
  65. {
  66. issuer = keys[0];
  67. }
  68. else if (location.Equals("China"))
  69. {
  70. issuer = keys[1];
  71. }
  72. else if (location.Equals("Global-Dep"))
  73. {
  74. issuer = keys[2];
  75. }
  76. else if (location.Equals("Global-Test"))
  77. {
  78. issuer = keys[2];
  79. }
  80. else if (location.Equals("Global"))
  81. {
  82. issuer = keys[3];
  83. }
  84. string jti = Guid.NewGuid().ToString();
  85. // 設定要加入到 JWT Token 中的聲明資訊(Claims)
  86. var payload = new JwtPayload {
  87. { JwtRegisteredClaimNames.Iss, issuer }, //發行者 iss: jwt签发者
  88. { JwtRegisteredClaimNames.Sub, id }, // APPID sub: jwt所面向的用户
  89. {JwtRegisteredClaimNames.Jti, jti},
  90. { "scope",scope}
  91. };
  92. // 建立一組對稱式加密的金鑰,主要用於 JWT 簽章之用
  93. var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(salt));
  94. // HmacSha256 有要求必須要大於 128 bits,所以 salt 不能太短,至少要 16 字元以上
  95. // https://stackoverflow.com/questions/47279947/idx10603-the-algorithm-hs256-requires-the-securitykey-keysize-to-be-greater
  96. var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
  97. var header = new JwtHeader(signingCredentials);
  98. var secToken = new JwtSecurityToken(header, payload);
  99. // 產出所需要的 JWT securityToken 物件,並取得序列化後的 Token 結果(字串格式)
  100. var tokenHandler = new JwtSecurityTokenHandler();
  101. //var securityToken = tokenHandler.CreateToken(tokenDescriptor);
  102. var serializeToken = tokenHandler.WriteToken(secToken);
  103. return (serializeToken,jti);
  104. }
  105. /// <summary>
  106. ///
  107. /// </summary>
  108. /// <param name="issuer">颁发者</param>
  109. /// <param name="id">第三方合作uuid</param>
  110. /// <param name="salt"></param>
  111. /// <param name="expire"></param>
  112. /// <returns></returns>
  113. public static (string jwt, string jti) CreateSchoolApiToken(string location, string id, string salt,List<int> auth, string schoolID = "")
  114. {
  115. string scope = "school";
  116. var keys = OpenApiJtwIssuer.OpenApiJtw签发者.GetDescriptionText().Split(',');
  117. string issuer = "";
  118. if (location.Equals("China-Dep"))
  119. {
  120. issuer = keys[0];
  121. }
  122. else if (location.Equals("China-Test"))
  123. {
  124. issuer = keys[0];
  125. }
  126. else if (location.Equals("China"))
  127. {
  128. issuer = keys[1];
  129. }
  130. else if (location.Equals("Global-Dep"))
  131. {
  132. issuer = keys[2];
  133. }
  134. else if (location.Equals("Global-Test"))
  135. {
  136. issuer = keys[2];
  137. }
  138. else if (location.Equals("Global"))
  139. {
  140. issuer = keys[3];
  141. }
  142. string jti = Guid.NewGuid().ToString();
  143. // 設定要加入到 JWT Token 中的聲明資訊(Claims)
  144. var payload = new JwtPayload {
  145. { JwtRegisteredClaimNames.Iss, issuer }, //發行者 iss: jwt签发者
  146. { JwtRegisteredClaimNames.Sub, id }, // APPID sub: jwt所面向的用户
  147. {JwtRegisteredClaimNames.Jti, jti},
  148. { "scope",scope},
  149. { "auth",auth},
  150. { JwtRegisteredClaimNames.Azp,schoolID}, // 學校簡碼,如果有的話
  151. };
  152. // 建立一組對稱式加密的金鑰,主要用於 JWT 簽章之用
  153. var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(salt));
  154. // HmacSha256 有要求必須要大於 128 bits,所以 salt 不能太短,至少要 16 字元以上
  155. // https://stackoverflow.com/questions/47279947/idx10603-the-algorithm-hs256-requires-the-securitykey-keysize-to-be-greater
  156. var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
  157. var header = new JwtHeader(signingCredentials);
  158. var secToken = new JwtSecurityToken(header, payload);
  159. // 產出所需要的 JWT securityToken 物件,並取得序列化後的 Token 結果(字串格式)
  160. var tokenHandler = new JwtSecurityTokenHandler();
  161. //var securityToken = tokenHandler.CreateToken(tokenDescriptor);
  162. var serializeToken = tokenHandler.WriteToken(secToken);
  163. return (serializeToken, jti);
  164. }
  165. public static string CreateApiToken(string issuer, string id, string salt, string name, List<int> auth, string schoolID = "", int expire = 1)
  166. {
  167. // 設定要加入到 JWT Token 中的聲明資訊(Claims)
  168. var payload = new JwtPayload {
  169. { JwtRegisteredClaimNames.Iss, issuer }, //發行者 iss: jwt签发者
  170. { JwtRegisteredClaimNames.Sub, id }, // APPID sub: jwt所面向的用户
  171. { JwtRegisteredClaimNames.Aud, "" }, // aud: 接收jwt的一方
  172. { JwtRegisteredClaimNames.Azp,schoolID}, // 學校簡碼,如果有的話
  173. {JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString() },
  174. { "name",name}, // 用戶的顯示名稱
  175. //{ JwtRegisteredClaimNames.Exp,DateTimeOffset.UtcNow.AddHours(expire).ToUnixTimeSeconds()}, // 到期的時間,必須為數字
  176. //{ "name",name}, // 用戶的顯示名稱
  177. //{ "picture",picture}, // 用戶頭像
  178. { "auth",auth}, // 登入者的角色,角色類型 (Admin、Teacher、Student)
  179. // { "permissions",permissions} //登入者的權限請求
  180. };
  181. // 建立一組對稱式加密的金鑰,主要用於 JWT 簽章之用
  182. var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(salt));
  183. // HmacSha256 有要求必須要大於 128 bits,所以 salt 不能太短,至少要 16 字元以上
  184. // https://stackoverflow.com/questions/47279947/idx10603-the-algorithm-hs256-requires-the-securitykey-keysize-to-be-greater
  185. var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
  186. var header = new JwtHeader(signingCredentials);
  187. var secToken = new JwtSecurityToken(header, payload);
  188. // 產出所需要的 JWT securityToken 物件,並取得序列化後的 Token 結果(字串格式)
  189. var tokenHandler = new JwtSecurityTokenHandler();
  190. //var securityToken = tokenHandler.CreateToken(tokenDescriptor);
  191. var serializeToken = tokenHandler.WriteToken(secToken);
  192. return serializeToken;
  193. }
  194. public static bool ValidateApiToken(string token, string salt)
  195. {
  196. try
  197. {
  198. var handler = new JwtSecurityTokenHandler();
  199. var validationParameters = new TokenValidationParameters
  200. {
  201. RequireExpirationTime = false,
  202. ValidateIssuer = false,
  203. ValidateAudience = false,
  204. IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(salt)),
  205. ValidateLifetime = false,
  206. //LifetimeValidator = LifetimeValidator,
  207. ClockSkew = TimeSpan.Zero
  208. };
  209. ClaimsPrincipal principal = handler.ValidateToken(token, validationParameters, out SecurityToken securityToken);
  210. return true;
  211. }
  212. catch (Exception)
  213. {
  214. //Trace.WriteLine(ex.Message);
  215. return false;
  216. }
  217. }
  218. public static bool ValidateAuthToken(string token, string salt)
  219. {
  220. try
  221. {
  222. var handler = new JwtSecurityTokenHandler();
  223. var validationParameters = new TokenValidationParameters
  224. {
  225. RequireExpirationTime = true,
  226. ValidateIssuer = false,
  227. ValidateAudience = false,
  228. IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(salt)),
  229. ValidateLifetime = false,
  230. //LifetimeValidator = LifetimeValidator,
  231. ClockSkew = TimeSpan.Zero
  232. };
  233. ClaimsPrincipal principal = handler.ValidateToken(token, validationParameters, out SecurityToken securityToken);
  234. return true;
  235. }
  236. catch (Exception)
  237. {
  238. //Trace.WriteLine(ex.Message);
  239. return false;
  240. }
  241. }
  242. /// <summary>
  243. /// 第三方登录后的id_token
  244. /// </summary>
  245. /// <param name="issuser"></param>
  246. /// <param name="id"></param>
  247. /// <param name="name"></param>
  248. /// <param name="picture"></param>
  249. /// <param name="webSite"></param>
  250. /// <param name="salt"></param>
  251. /// <param name="roles"></param>
  252. /// <param name="permissions"></param>
  253. /// <param name="expire"></param>
  254. /// <returns></returns>
  255. public static string CreateBizLoginAuthToken(string issuser, string id, string name, string picture, string webSite, string salt, string[] roles = null, string[] permissions = null, int expire = 1)
  256. {
  257. var payload = new JwtPayload
  258. {
  259. { JwtRegisteredClaimNames.Iss,issuser}, //发行者
  260. { JwtRegisteredClaimNames.Sub,id}, //用户ID
  261. { JwtRegisteredClaimNames.Exp,DateTimeOffset.UtcNow.AddHours(expire).ToUnixTimeSeconds()},//到期时间
  262. { "name",name},//用户显示名称
  263. { "picture",picture}, // 用户头像
  264. { "roles",roles}, //登陆者的角色, (admin、dea)
  265. { "permissions",permissions}, //登陆者的权限
  266. { JwtRegisteredClaimNames.Website,webSite}, // 平台站点
  267. };
  268. // 建立加密的秘钥
  269. var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(salt));
  270. // HmacSha256 有要求必须要大于 128 bits,所以 salt 不能太短,至少要 16 字元以上
  271. // https://stackoverflow.com/questions/47279947/idx10603-the-algorithm-hs256-requires-the-securitykey-keysize-to-be-greater
  272. var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
  273. var header = new JwtHeader(signingCredentials);
  274. var secToken = new JwtSecurityToken(header, payload);
  275. // 產出所需要的 JWT securityToken 物件,並取得序列化後的 Token 結果(字串格式)
  276. var tokenHandler = new JwtSecurityTokenHandler();
  277. //var securityToken = tokenHandler.CreateToken(tokenDescriptor);
  278. var serializeToken = tokenHandler.WriteToken(secToken);
  279. return serializeToken;
  280. }
  281. }
  282. public enum OpenApiJtwIssuer
  283. {
  284. [Description("open-test.teammodel.cn,open.teammodel.cn,open-test.teammodel.net,open.teammodel.net")]
  285. OpenApiJtw签发者,
  286. }
  287. }