JwtAuthExtension.cs 17 KB

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