JwtAuthExtension.cs 16 KB

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