JwtAuthExtension.cs 16 KB

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