using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; using System; using System.Threading.Tasks; using System.Security.Claims; using System.IdentityModel.Tokens.Jwt; using System.Collections.Generic; using System.Text; using System.ComponentModel; namespace TEAMModelOS.SDK.Extension { public static class JwtAuthExtension { 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) { // 設定要加入到 JWT Token 中的聲明資訊(Claims) var payload = new JwtPayload { { JwtRegisteredClaimNames.Iss, issuer }, //發行者 { JwtRegisteredClaimNames.Sub, id }, // 用戶ID { JwtRegisteredClaimNames.Azp,schoolID}, // 學校簡碼,如果有的話 { JwtRegisteredClaimNames.Exp,DateTimeOffset.UtcNow.AddHours(expire).ToUnixTimeSeconds()}, // 到期的時間,必須為數字 { "name",name}, // 用戶的顯示名稱 { "picture",picture}, // 用戶頭像 { "roles",roles}, // 登入者的角色,角色類型 (Admin、Teacher、Student) { "permissions",permissions}, //登入者的權限請求 { "standard",standard} ,//登入者的能力点标准 { "scope",scope}, //登入者的入口类型。 (teacher 教师端登录的醍摩豆ID、tmduser学生端登录的醍摩豆ID、student学生端登录校内账号的学生ID) { "area",areaId==null?"":areaId}, { JwtRegisteredClaimNames.Website,Website}, }; //学生入学年 if (year!=-1) { payload.Add("year", year); } // 建立一組對稱式加密的金鑰,主要用於 JWT 簽章之用 var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(salt)); // HmacSha256 有要求必須要大於 128 bits,所以 salt 不能太短,至少要 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 header = new JwtHeader(signingCredentials); var secToken = new JwtSecurityToken(header, payload); // 產出所需要的 JWT securityToken 物件,並取得序列化後的 Token 結果(字串格式) var tokenHandler = new JwtSecurityTokenHandler(); //var securityToken = tokenHandler.CreateToken(tokenDescriptor); var serializeToken = tokenHandler.WriteToken(secToken); return serializeToken; } /// /// /// /// 颁发者 /// 第三方合作uuid /// /// /// public static (string jwt , string jti) CreateBusinessApiToken(string location, string id, string salt,int customize = 0 ) { string scope = "business"; if (customize != 0) scope = "customize"; var keys = OpenApiJtwIssuer.OpenApiJtw签发者.GetDescriptionText().Split(','); string issuer = ""; if (location.Equals("China-Dep")) { issuer = keys[0]; } else if (location.Equals("China-Test")) { issuer = keys[0]; } else if (location.Equals("China")) { issuer = keys[1]; } else if (location.Equals("Global-Dep")) { issuer = keys[2]; } else if (location.Equals("Global-Test")) { issuer = keys[2]; } else if (location.Equals("Global")) { issuer = keys[3]; } string jti = Guid.NewGuid().ToString(); // 設定要加入到 JWT Token 中的聲明資訊(Claims) var payload = new JwtPayload { { JwtRegisteredClaimNames.Iss, issuer }, //發行者 iss: jwt签发者 { JwtRegisteredClaimNames.Sub, id }, // APPID sub: jwt所面向的用户 {JwtRegisteredClaimNames.Jti, jti}, { "scope",scope} }; // 建立一組對稱式加密的金鑰,主要用於 JWT 簽章之用 var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(salt)); // HmacSha256 有要求必須要大於 128 bits,所以 salt 不能太短,至少要 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 header = new JwtHeader(signingCredentials); var secToken = new JwtSecurityToken(header, payload); // 產出所需要的 JWT securityToken 物件,並取得序列化後的 Token 結果(字串格式) var tokenHandler = new JwtSecurityTokenHandler(); //var securityToken = tokenHandler.CreateToken(tokenDescriptor); var serializeToken = tokenHandler.WriteToken(secToken); return (serializeToken,jti); } /// /// /// /// 颁发者 /// 第三方合作uuid /// /// /// public static (string jwt, string jti) CreateSchoolApiToken(string location, string id, string salt,List auth, string schoolID = "") { string scope = "school"; var keys = OpenApiJtwIssuer.OpenApiJtw签发者.GetDescriptionText().Split(','); string issuer = ""; if (location.Equals("China-Dep")) { issuer = keys[0]; } else if (location.Equals("China-Test")) { issuer = keys[0]; } else if (location.Equals("China")) { issuer = keys[1]; } else if (location.Equals("Global-Dep")) { issuer = keys[2]; } else if (location.Equals("Global-Test")) { issuer = keys[2]; } else if (location.Equals("Global")) { issuer = keys[3]; } string jti = Guid.NewGuid().ToString(); // 設定要加入到 JWT Token 中的聲明資訊(Claims) var payload = new JwtPayload { { JwtRegisteredClaimNames.Iss, issuer }, //發行者 iss: jwt签发者 { JwtRegisteredClaimNames.Sub, id }, // APPID sub: jwt所面向的用户 {JwtRegisteredClaimNames.Jti, jti}, { "scope",scope}, { "auth",auth}, { JwtRegisteredClaimNames.Azp,schoolID}, // 學校簡碼,如果有的話 }; // 建立一組對稱式加密的金鑰,主要用於 JWT 簽章之用 var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(salt)); // HmacSha256 有要求必須要大於 128 bits,所以 salt 不能太短,至少要 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 header = new JwtHeader(signingCredentials); var secToken = new JwtSecurityToken(header, payload); // 產出所需要的 JWT securityToken 物件,並取得序列化後的 Token 結果(字串格式) var tokenHandler = new JwtSecurityTokenHandler(); //var securityToken = tokenHandler.CreateToken(tokenDescriptor); var serializeToken = tokenHandler.WriteToken(secToken); return (serializeToken, jti); } public static string CreateApiToken(string issuer, string id, string salt, string name, List auth, string schoolID = "", int expire = 1) { // 設定要加入到 JWT Token 中的聲明資訊(Claims) var payload = new JwtPayload { { JwtRegisteredClaimNames.Iss, issuer }, //發行者 iss: jwt签发者 { JwtRegisteredClaimNames.Sub, id }, // APPID sub: jwt所面向的用户 { JwtRegisteredClaimNames.Aud, "" }, // aud: 接收jwt的一方 { JwtRegisteredClaimNames.Azp,schoolID}, // 學校簡碼,如果有的話 {JwtRegisteredClaimNames.Jti,Guid.NewGuid().ToString() }, { "name",name}, // 用戶的顯示名稱 //{ JwtRegisteredClaimNames.Exp,DateTimeOffset.UtcNow.AddHours(expire).ToUnixTimeSeconds()}, // 到期的時間,必須為數字 //{ "name",name}, // 用戶的顯示名稱 //{ "picture",picture}, // 用戶頭像 { "auth",auth}, // 登入者的角色,角色類型 (Admin、Teacher、Student) // { "permissions",permissions} //登入者的權限請求 }; // 建立一組對稱式加密的金鑰,主要用於 JWT 簽章之用 var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(salt)); // HmacSha256 有要求必須要大於 128 bits,所以 salt 不能太短,至少要 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 header = new JwtHeader(signingCredentials); var secToken = new JwtSecurityToken(header, payload); // 產出所需要的 JWT securityToken 物件,並取得序列化後的 Token 結果(字串格式) var tokenHandler = new JwtSecurityTokenHandler(); //var securityToken = tokenHandler.CreateToken(tokenDescriptor); var serializeToken = tokenHandler.WriteToken(secToken); return serializeToken; } public static bool ValidateApiToken(string token, string salt) { try { var handler = new JwtSecurityTokenHandler(); var validationParameters = new TokenValidationParameters { RequireExpirationTime = false, ValidateIssuer = false, ValidateAudience = false, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(salt)), ValidateLifetime = false, //LifetimeValidator = LifetimeValidator, ClockSkew = TimeSpan.Zero }; ClaimsPrincipal principal = handler.ValidateToken(token, validationParameters, out SecurityToken securityToken); return true; } catch (Exception) { //Trace.WriteLine(ex.Message); return false; } } public static bool ValidateAuthToken(string token, string salt) { try { var handler = new JwtSecurityTokenHandler(); var validationParameters = new TokenValidationParameters { RequireExpirationTime = true, ValidateIssuer = false, ValidateAudience = false, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(salt)), ValidateLifetime = false, //LifetimeValidator = LifetimeValidator, ClockSkew = TimeSpan.Zero }; ClaimsPrincipal principal = handler.ValidateToken(token, validationParameters, out SecurityToken securityToken); return true; } catch (Exception) { //Trace.WriteLine(ex.Message); return false; } } /// /// 刷新token,不验证过期时间 /// /// /// /// public static bool ValidateAuthTokenRefresh(string token, string salt) { try { var handler = new JwtSecurityTokenHandler(); var validationParameters = new TokenValidationParameters { RequireExpirationTime = false, ValidateIssuer = false, ValidateAudience = false, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(salt)), ValidateLifetime = true, ClockSkew = TimeSpan.Zero }; ClaimsPrincipal principal = handler.ValidateToken(token, validationParameters, out SecurityToken securityToken); return true; } catch (Exception) { //Trace.WriteLine(ex.Message); return false; } } /// /// 第三方登录后的id_token /// /// /// /// /// /// /// /// /// /// /// 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) { var payload = new JwtPayload { { JwtRegisteredClaimNames.Iss,issuser}, //发行者 { JwtRegisteredClaimNames.Sub,id}, //用户ID { JwtRegisteredClaimNames.Exp,DateTimeOffset.UtcNow.AddHours(expire).ToUnixTimeSeconds()},//到期时间 { "name",name},//用户显示名称 { "picture",picture}, // 用户头像 { "roles",roles}, //登陆者的角色, (admin、dea) { "permissions",permissions}, //登陆者的权限 { JwtRegisteredClaimNames.Website,webSite}, // 平台站点 }; // 建立加密的秘钥 var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(salt)); // HmacSha256 有要求必须要大于 128 bits,所以 salt 不能太短,至少要 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 header = new JwtHeader(signingCredentials); var secToken = new JwtSecurityToken(header, payload); // 產出所需要的 JWT securityToken 物件,並取得序列化後的 Token 結果(字串格式) var tokenHandler = new JwtSecurityTokenHandler(); //var securityToken = tokenHandler.CreateToken(tokenDescriptor); var serializeToken = tokenHandler.WriteToken(secToken); return serializeToken; } } public enum OpenApiJtwIssuer { [Description("open-test.teammodel.cn,open.teammodel.cn,open-test.teammodel.net,open.teammodel.net")] OpenApiJtw签发者, } }