当前位置:   article > 正文

.Net Core 前后端分离之接口数据传输加密_core6 接口加密

core6 接口加密

本文主要介绍以非对称加密+对称加密结合的方式对post请求的接口数据进行加密。

实现思路

  • 前端对post请求接口进行加密,先用非对称加密方式(RSA)加密对称加密的密钥,然后对称加密(AES)数据包。
  • 后台在过滤器中进行数据包解密操作。

一.前端加密

  • 此处以vue为例,只针对post请求
  • vue需要引入JSEncrypt
  1. let data = parameter.data;
  2. let suijiNum = randomNumber();
  3. data = JSON.stringify(data);
  4. console.log("原始数据:" + data);
  5. data = encrypt(data, suijiNum);//非对称方式加密数据体
  6. data = Qs.stringify({data});
  7. axios.post(parameter.url, data, { headers: { 'token': encrypt(getToken(), suijiNum), "aeskey": rsaEncryp(suijiNum) } })
  8. .then(res => {
  9. if (res.data.statusCode != 200) {
  10. if (res.data.statusCode == 401) {
  11. Message({
  12. message: '登录信息已过期,请重新登录',
  13. type: 'error',
  14. duration: 5 * 1000
  15. });
  16. setTimeout(_ => {
  17. store.dispatch('LogOut').then(() => {
  18. location.reload() // 为了重新实例化vue- router对象避免bug
  19. })
  20. }, 3000)
  21. } else {
  22. Message({
  23. message: res.data.msg,
  24. type: 'error',
  25. duration: 5 * 1000
  26. });
  27. }
  28. } else {
  29. parameter.fun(res);
  30. }
  31. })
  32. .catch(err => {
  33. console.log(err);
  34. Message({
  35. message: err.message,
  36. type: 'error',
  37. duration: 5 * 1000
  38. });
  39. });
  40. }
  • 辅助代码
  1. function rsaEncryp(data) {
  2. let encryption = new JSEncrypt()
  3. encryption.setPublicKey('MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoQh0wEqx/R2H1v00IU12Oc30fosRC/frhH89L6G+fzeaqI19MYQhEPMU13wpeqRONCUta+2iC1sgCNQ9qGGf19yGdZUfueaB1Nu9rdueQKXgVurGHJ+5N71UFm+OP1XcnFUCK4wT5d7ZIifXxuqLehP9Ts6sNjhVfa+yU+VjF5HoIe69OJEPo7OxRZcRTe17khc93Ic+PfyqswQJJlY/bgpcLJQnM+QuHmxNtF7/FpAx9YEQsShsGpVo7JaKgLo+s6AFoJ4QldQKir2vbN9vcKRbG3piElPilWDpjXQkOJZhUloh/jd7QrKFimZFldJ1r6Q59QYUyGKZARUe0KZpMQIDAQAB')
  4. let newData = encryption.encrypt(data)
  5. return newData
  6. }
  7. function randomNumber() {
  8. return (('0000000000000000' + Math.floor(Math.random() * 9999999999999999)).slice(-16));
  9. }

 

二.后台解密

   (1).后台在过滤器中对数据进行解密操作,创建过滤器DataDecryptFilter。

首先用私钥解密对称加密的密钥aeskey,然后再用aeskey去解密数据体

  1. /// <summary>
  2. /// 数据解密过滤器
  3. /// 前端只对post请求接口进行加密(先用非对称加密方式(RSA)加密对称加密的密钥,然后对称加密(AES)数据包)
  4. /// create by LiuCheng 2019.5.29
  5. /// </summary>
  6. public class DataDecryptFilter : ActionFilterAttribute
  7. {
  8. public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
  9. {
  10. var noEncrypt = ConfigHelper.GetSectionValue("NoEncrypt");//白名单
  11. var method = context.HttpContext.Request.Method;
  12. if (method == "POST" && !noEncrypt.Contains(context.HttpContext.Request.Path))
  13. {
  14. string aeskey = context.HttpContext.Request.Headers["aeskey"];
  15. if (aeskey.Length > 36)//判断aeskey是否已经解密,若未解密则先进行解密操作
  16. {
  17. var rsaHelper = new RSAHelper(RSAType.RSA2, Encoding.UTF8);
  18. aeskey = rsaHelper.Decrypt(aeskey);
  19. }
  20. //解密数据包
  21. var data = context.HttpContext.Request.Form.FirstOrDefault().Value;
  22. var dataJson = AESHelper.AesDecrypt(data, aeskey);
  23. if (string.IsNullOrWhiteSpace(dataJson))
  24. {
  25. context.Result = AjaxHelper.JsonResult(HttpStatusCode.BadRequest, " 数据请求不合法!");
  26. return;
  27. }
  28. if (context.ActionArguments.Values.Count > 0)
  29. {
  30. //-----------model接收模式----------//
  31. var type = context.ActionArguments.Values.ToList()[0].GetType();
  32. PropertyInfo[] ps = type.GetProperties();
  33. var model = context.ActionArguments.Values.ToList()[0];
  34. var dy = JsonConvert.DeserializeObject(dataJson, type);
  35. var type2 = dy.GetType();
  36. PropertyInfo[] ps2 = type2.GetProperties();
  37. foreach (PropertyInfo i in ps)
  38. {
  39. foreach (PropertyInfo i2 in ps2)
  40. {
  41. var value = i2.GetValue(dy, null);
  42. if (i.Name == i2.Name && value != null)
  43. {
  44. i.SetValue(model, value, null);
  45. }
  46. }
  47. }
  48. }
  49. else
  50. {
  51. //-----------变量接收模式----------//
  52. var dy = (JObject)JsonConvert.DeserializeObject(dataJson);
  53. var parameterslist = context.ActionDescriptor.Parameters.ToList();
  54. foreach (var item in parameterslist)
  55. {
  56. if (dy[item.Name] == null)
  57. continue;
  58. var vaule = ConvertObject(dy[item.Name].ToString(), item.ParameterType);
  59. context.ActionArguments.Add(item.Name, vaule);
  60. }
  61. }
  62. }
  63. await base.OnActionExecutionAsync(context, next);
  64. }
  65. /// <summary>
  66. /// 将一个对象转换为指定类型
  67. /// </summary>
  68. /// <param name="obj">待转换的对象</param>
  69. /// <param name="type">目标类型</param>
  70. /// <returns>转换后的对象</returns>
  71. private object ConvertObject(object obj, Type type)
  72. {
  73. if (type == null) return obj;
  74. if (obj == null) return type.IsValueType ? Activator.CreateInstance(type) : null;
  75. Type underlyingType = Nullable.GetUnderlyingType(type);
  76. if (type.IsAssignableFrom(obj.GetType())) // 如果待转换对象的类型与目标类型兼容,则无需转换
  77. {
  78. return obj;
  79. }
  80. else if ((underlyingType ?? type).IsEnum) // 如果待转换的对象的基类型为枚举
  81. {
  82. if (underlyingType != null && string.IsNullOrEmpty(obj.ToString())) // 如果目标类型为可空枚举,并且待转换对象为null 则直接返回null
  83. {
  84. return null;
  85. }
  86. else
  87. {
  88. return Enum.Parse(underlyingType ?? type, obj.ToString());
  89. }
  90. }
  91. else if (typeof(IConvertible).IsAssignableFrom(underlyingType ?? type)) // 如果目标类型的基类型实现了IConvertible,则直接转换
  92. {
  93. try
  94. {
  95. return Convert.ChangeType(obj, underlyingType ?? type, null);
  96. }
  97. catch
  98. {
  99. return underlyingType == null ? Activator.CreateInstance(type) : null;
  100. }
  101. }
  102. else
  103. {
  104. TypeConverter converter = TypeDescriptor.GetConverter(type);
  105. if (converter.CanConvertFrom(obj.GetType()))
  106. {
  107. return converter.ConvertFrom(obj);
  108. }
  109. ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);
  110. if (constructor != null)
  111. {
  112. object o = constructor.Invoke(null);
  113. PropertyInfo[] propertys = type.GetProperties();
  114. Type oldType = obj.GetType();
  115. foreach (PropertyInfo property in propertys)
  116. {
  117. PropertyInfo p = oldType.GetProperty(property.Name);
  118. if (property.CanWrite && p != null && p.CanRead)
  119. {
  120. property.SetValue(o, ConvertObject(p.GetValue(obj, null), property.PropertyType), null);
  121. }
  122. }
  123. return o;
  124. }
  125. }
  126. return obj;
  127. }
  128. }

(2).对称加密工具类

  1. /// <summary>
  2. /// 对称加密
  3. /// </summary>
  4. public class AESHelper
  5. {
  6. /// <summary>
  7. /// AES加密
  8. /// </summary>
  9. /// <param name="str"></param>
  10. /// <param name="key"></param>
  11. /// <returns></returns>
  12. public static string AesEncrypt(string str, string key)
  13. {
  14. string result;
  15. try
  16. {
  17. if (string.IsNullOrEmpty(str))
  18. {
  19. result = null;
  20. }
  21. else
  22. {
  23. byte[] bytes = Encoding.UTF8.GetBytes(str);
  24. RijndaelManaged rijndaelManaged = new RijndaelManaged
  25. {
  26. Key = Encoding.UTF8.GetBytes(key),
  27. Mode = CipherMode.ECB,
  28. Padding = PaddingMode.PKCS7
  29. };
  30. ICryptoTransform cryptoTransform = rijndaelManaged.CreateEncryptor();
  31. byte[] array = cryptoTransform.TransformFinalBlock(bytes, 0, bytes.Length);
  32. result = Convert.ToBase64String(array, 0, array.Length);
  33. return result;
  34. }
  35. }
  36. catch (Exception ex)
  37. {
  38. result = null;
  39. System.Console.WriteLine(ex);
  40. }
  41. return result;
  42. }
  43. /// <summary>
  44. /// AES解密
  45. /// </summary>
  46. /// <param name="str"></param>
  47. /// <param name="key"></param>
  48. /// <returns></returns>
  49. public static string AesDecrypt(string str, string key)
  50. {
  51. if (string.IsNullOrEmpty(str))
  52. {
  53. return null;
  54. }
  55. string result;
  56. try
  57. {
  58. byte[] array = Convert.FromBase64String(str);
  59. RijndaelManaged rijndaelManaged = new RijndaelManaged
  60. {
  61. Key = Encoding.UTF8.GetBytes(key),
  62. Mode = CipherMode.ECB,
  63. Padding = PaddingMode.PKCS7
  64. };
  65. ICryptoTransform cryptoTransform = rijndaelManaged.CreateDecryptor();
  66. byte[] bytes = cryptoTransform.TransformFinalBlock(array, 0, array.Length);
  67. result = Encoding.UTF8.GetString(bytes);
  68. }
  69. catch (Exception ex)
  70. {
  71. result = null;
  72. Console.WriteLine(ex);
  73. }
  74. return result;
  75. }
  76. }

(3).非对称加密工具类

  1. /// <summary>
  2. /// 非对称加密
  3. /// </summary>
  4. public class RSAHelper
  5. {
  6. private readonly RSA _privateKeyRsaProvider;
  7. private readonly RSA _publicKeyRsaProvider;
  8. private readonly HashAlgorithmName _hashAlgorithmName;
  9. private readonly Encoding _encoding;
  10. private readonly string privateKey = ConfigHelper.GetSectionValue("privateKey");
  11. private readonly string publicKey = ConfigHelper.GetSectionValue("publicKey");
  12. /// <summary>
  13. /// 实例化RSAHelper
  14. /// </summary>
  15. /// <param name="rsaType">加密算法类型 RSA SHA1;RSA2 SHA256 密钥长度至少为2048</param>
  16. /// <param name="encoding">编码类型</param>
  17. public RSAHelper(RSAType rsaType, Encoding encoding)
  18. {
  19. _encoding = encoding;
  20. if (!string.IsNullOrEmpty(privateKey))
  21. {
  22. _privateKeyRsaProvider = CreateRsaProviderFromPrivateKey(privateKey);
  23. }
  24. if (!string.IsNullOrEmpty(publicKey))
  25. {
  26. _publicKeyRsaProvider = CreateRsaProviderFromPublicKey(publicKey);
  27. }
  28. _hashAlgorithmName = rsaType == RSAType.RSA ? HashAlgorithmName.SHA1 : HashAlgorithmName.SHA256;
  29. }
  30. #region 使用私钥签名
  31. /// <summary>
  32. /// 使用私钥签名
  33. /// </summary>
  34. /// <param name="data">原始数据</param>
  35. /// <returns></returns>
  36. public string Sign(string data)
  37. {
  38. byte[] dataBytes = _encoding.GetBytes(data);
  39. var signatureBytes = _privateKeyRsaProvider.SignData(dataBytes, _hashAlgorithmName, RSASignaturePadding.Pkcs1);
  40. return Convert.ToBase64String(signatureBytes);
  41. }
  42. #endregion
  43. #region 使用公钥验证签名
  44. /// <summary>
  45. /// 使用公钥验证签名
  46. /// </summary>
  47. /// <param name="data">原始数据</param>
  48. /// <param name="sign">签名</param>
  49. /// <returns></returns>
  50. public bool Verify(string data, string sign)
  51. {
  52. byte[] dataBytes = _encoding.GetBytes(data);
  53. byte[] signBytes = Convert.FromBase64String(sign);
  54. var verify = _publicKeyRsaProvider.VerifyData(dataBytes, signBytes, _hashAlgorithmName, RSASignaturePadding.Pkcs1);
  55. return verify;
  56. }
  57. #endregion
  58. #region 解密
  59. public string Decrypt(string cipherText)
  60. {
  61. if (_privateKeyRsaProvider == null)
  62. {
  63. throw new Exception("_privateKeyRsaProvider is null");
  64. }
  65. return Encoding.UTF8.GetString(_privateKeyRsaProvider.Decrypt(Convert.FromBase64String(cipherText), RSAEncryptionPadding.Pkcs1));
  66. }
  67. #endregion
  68. #region 加密
  69. public string Encrypt(string text)
  70. {
  71. if (_publicKeyRsaProvider == null)
  72. {
  73. throw new Exception("_publicKeyRsaProvider is null");
  74. }
  75. return Convert.ToBase64String(_publicKeyRsaProvider.Encrypt(Encoding.UTF8.GetBytes(text), RSAEncryptionPadding.Pkcs1));
  76. }
  77. #endregion
  78. #region 使用私钥创建RSA实例
  79. public RSA CreateRsaProviderFromPrivateKey(string privateKey)
  80. {
  81. var privateKeyBits = Convert.FromBase64String(privateKey);
  82. var rsa = RSA.Create();
  83. var rsaParameters = new RSAParameters();
  84. using (BinaryReader binr = new BinaryReader(new MemoryStream(privateKeyBits)))
  85. {
  86. byte bt = 0;
  87. ushort twobytes = 0;
  88. twobytes = binr.ReadUInt16();
  89. if (twobytes == 0x8130)
  90. binr.ReadByte();
  91. else if (twobytes == 0x8230)
  92. binr.ReadInt16();
  93. else
  94. throw new Exception("Unexpected value read binr.ReadUInt16()");
  95. twobytes = binr.ReadUInt16();
  96. if (twobytes != 0x0102)
  97. throw new Exception("Unexpected version");
  98. bt = binr.ReadByte();
  99. if (bt != 0x00)
  100. throw new Exception("Unexpected value read binr.ReadByte()");
  101. rsaParameters.Modulus = binr.ReadBytes(GetIntegerSize(binr));
  102. rsaParameters.Exponent = binr.ReadBytes(GetIntegerSize(binr));
  103. rsaParameters.D = binr.ReadBytes(GetIntegerSize(binr));
  104. rsaParameters.P = binr.ReadBytes(GetIntegerSize(binr));
  105. rsaParameters.Q = binr.ReadBytes(GetIntegerSize(binr));
  106. rsaParameters.DP = binr.ReadBytes(GetIntegerSize(binr));
  107. rsaParameters.DQ = binr.ReadBytes(GetIntegerSize(binr));
  108. rsaParameters.InverseQ = binr.ReadBytes(GetIntegerSize(binr));
  109. }
  110. rsa.ImportParameters(rsaParameters);
  111. return rsa;
  112. }
  113. #endregion
  114. #region 使用公钥创建RSA实例
  115. public RSA CreateRsaProviderFromPublicKey(string publicKeyString)
  116. {
  117. // encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
  118. byte[] seqOid = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
  119. byte[] seq = new byte[15];
  120. var x509Key = Convert.FromBase64String(publicKeyString);
  121. // --------- Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob ------
  122. using (MemoryStream mem = new MemoryStream(x509Key))
  123. {
  124. using (BinaryReader binr = new BinaryReader(mem)) //wrap Memory Stream with BinaryReader for easy reading
  125. {
  126. byte bt = 0;
  127. ushort twobytes = 0;
  128. twobytes = binr.ReadUInt16();
  129. if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
  130. binr.ReadByte(); //advance 1 byte
  131. else if (twobytes == 0x8230)
  132. binr.ReadInt16(); //advance 2 bytes
  133. else
  134. return null;
  135. seq = binr.ReadBytes(15); //read the Sequence OID
  136. if (!CompareBytearrays(seq, seqOid)) //make sure Sequence for OID is correct
  137. return null;
  138. twobytes = binr.ReadUInt16();
  139. if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81)
  140. binr.ReadByte(); //advance 1 byte
  141. else if (twobytes == 0x8203)
  142. binr.ReadInt16(); //advance 2 bytes
  143. else
  144. return null;
  145. bt = binr.ReadByte();
  146. if (bt != 0x00) //expect null byte next
  147. return null;
  148. twobytes = binr.ReadUInt16();
  149. if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
  150. binr.ReadByte(); //advance 1 byte
  151. else if (twobytes == 0x8230)
  152. binr.ReadInt16(); //advance 2 bytes
  153. else
  154. return null;
  155. twobytes = binr.ReadUInt16();
  156. byte lowbyte = 0x00;
  157. byte highbyte = 0x00;
  158. if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81)
  159. lowbyte = binr.ReadByte(); // read next bytes which is bytes in modulus
  160. else if (twobytes == 0x8202)
  161. {
  162. highbyte = binr.ReadByte(); //advance 2 bytes
  163. lowbyte = binr.ReadByte();
  164. }
  165. else
  166. return null;
  167. byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; //reverse byte order since asn.1 key uses big endian order
  168. int modsize = BitConverter.ToInt32(modint, 0);
  169. int firstbyte = binr.PeekChar();
  170. if (firstbyte == 0x00)
  171. { //if first byte (highest order) of modulus is zero, don't include it
  172. binr.ReadByte(); //skip this null byte
  173. modsize -= 1; //reduce modulus buffer size by 1
  174. }
  175. byte[] modulus = binr.ReadBytes(modsize); //read the modulus bytes
  176. if (binr.ReadByte() != 0x02) //expect an Integer for the exponent data
  177. return null;
  178. int expbytes = (int)binr.ReadByte(); // should only need one byte for actual exponent data (for all useful values)
  179. byte[] exponent = binr.ReadBytes(expbytes);
  180. // ------- create RSACryptoServiceProvider instance and initialize with public key -----
  181. var rsa = RSA.Create();
  182. RSAParameters rsaKeyInfo = new RSAParameters
  183. {
  184. Modulus = modulus,
  185. Exponent = exponent
  186. };
  187. rsa.ImportParameters(rsaKeyInfo);
  188. return rsa;
  189. }
  190. }
  191. }
  192. #endregion
  193. #region 导入密钥算法
  194. private int GetIntegerSize(BinaryReader binr)
  195. {
  196. byte bt = 0;
  197. int count = 0;
  198. bt = binr.ReadByte();
  199. if (bt != 0x02)
  200. return 0;
  201. bt = binr.ReadByte();
  202. if (bt == 0x81)
  203. count = binr.ReadByte();
  204. else
  205. if (bt == 0x82)
  206. {
  207. var highbyte = binr.ReadByte();
  208. var lowbyte = binr.ReadByte();
  209. byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
  210. count = BitConverter.ToInt32(modint, 0);
  211. }
  212. else
  213. {
  214. count = bt;
  215. }
  216. while (binr.ReadByte() == 0x00)
  217. {
  218. count -= 1;
  219. }
  220. binr.BaseStream.Seek(-1, SeekOrigin.Current);
  221. return count;
  222. }
  223. private bool CompareBytearrays(byte[] a, byte[] b)
  224. {
  225. if (a.Length != b.Length)
  226. return false;
  227. int i = 0;
  228. foreach (byte c in a)
  229. {
  230. if (c != b[i])
  231. return false;
  232. i++;
  233. }
  234. return true;
  235. }
  236. #endregion
  237. }
  238. /// <summary>
  239. /// RSA算法类型
  240. /// </summary>
  241. public enum RSAType
  242. {
  243. /// <summary>
  244. /// SHA1
  245. /// </summary>
  246. RSA = 0,
  247. /// <summary>
  248. /// RSA2 密钥长度至少为2048
  249. /// SHA256
  250. /// </summary>
  251. RSA2
  252. }

(4).appsettings.json配置密钥

  1. "publicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoQh0wEqx/R2H1v00IU12Oc30fosRC/frhH89L6G+fzeaqI19MYQhEPMU13wpeqRONCUta+2iC1sgCNQ9qGGf19yGdZUfueaB1Nu9rdueQKXgVurGHJ+5N71UFm+OP1XcnFUCK4wT5d7ZIifXxuqLehP9Ts6sNjhVfa+yU+VjF5HoIe69OJEPo7OxRZcRTe17khc93Ic+PfyqswQJJlY/bgpcLJQnM+QuHmxNtF7/FpAx9YEQsShsGpVo7JaKgLo+s6AFoJ4QldQKir2vbN9vcKRbG3piElPilWDpjXQkOJZhUloh/jd7QrKFimZFldJ1r6Q59QYUyGKZARUe0KZpMQIDAQAB",
  2. "privateKey": "MIIEpAIBAAKCAQEAoQh0wEqx/R2H1v00IU12Oc30fosRC/frhH89L6G+fzeaqI19MYQhEPMU13wpeqRONCUta+2iC1sgCNQ9qGGf19yGdZUfueaB1Nu9rdueQKXgVurGHJ+5N71UFm+OP1XcnFUCK4wT5d7ZIifXxuqLehP9Ts6sNjhVfa+yU+VjF5HoIe69OJEPo7OxRZcRTe17khc93Ic+PfyqswQJJlY/bgpcLJQnM+QuHmxNtF7/FpAx9YEQsShsGpVo7JaKgLo+s6AFoJ4QldQKir2vbN9vcKRbG3piElPilWDpjXQkOJZhUloh/jd7QrKFimZFldJ1r6Q59QYUyGKZARUe0KZpMQIDAQABAoIBAQCRZLUlOUvjIVqYvhznRK1OG6p45s8JY1r+UnPIId2Bt46oSLeUkZvZVeCnfq9k0Bzb8AVGwVPhtPEDh73z3dEYcT/lwjLXAkyPB6gG5ZfI/vvC/k7JYV01+neFmktw2/FIJWjEMMF2dvLNZ/Pm4bX1Dz9SfD/45Hwr8wqrvRzvFZsj5qqOxv9RPAudOYwCwZskKp/GF+L+3Ycod1Wu98imzMZUH+L5dQuDGg3kvf3ljIAegTPoqYBg0imNPYY/EGoFKnbxlK5S5/5uAFb16dGJqAz3XQCz9Is/IWrOTu0etteqV2Ncs8uqPdjed+b0j8CMsr4U1xjwPQ8WwdaJtTkRAoGBANAndgiGZkCVcc9975/AYdgFp35W6D+hGQAZlL6DmnucUFdXbWa/x2rTSEXlkvgk9X/PxOptUYsLJkzysTgfDywZwuIXLm9B3oNmv3bVgPXsgDsvDfaHYCgz0nHK6NSrX2AeX3yO/dFuoZsuk+J+UyRigMqYj0wjmxUlqj183hinAoGBAMYMOBgF77OXRII7GAuEut/nBeh2sBrgyzR7FmJMs5kvRh6Ck8wp3ysgMvX4lxh1ep8iCw1R2cguqNATr1klOdsCTOE9RrhuvOp3JrYzuIAK6MpH/uBICy4w1rW2+gQySsHcH40r+tNaTFQ7dQ1tef//iy/IW8v8i0t+csztE1JnAoGABdtWYt8FOYP688+jUmdjWWSvVcq0NjYeMfaGTOX/DsNTL2HyXhW/Uq4nNnBDNmAz2CjMbZwt0y+5ICkj+2REVQVUinAEinTcAe5+LKXNPx4sbX3hcrJUbk0m+rSu4G0B/f5cyXBsi9wFCAzDdHgBduCepxSr04Sc9Hde1uQQi7kCgYB0U20HP0Vh+TG2RLuE2HtjVDD2L/CUeQEiXEHzjxXWnhvTg+MIAnggvpLwQwmMxkQ2ACr5sd/3YuCpB0bxV5o594nsqq9FWVYBaecFEjAGlWHSnqMoXWijwu/6X/VOTbP3VjH6G6ECT4GR4DKKpokIQrMgZ9DzaezvdOA9WesFdQKBgQCWfeOQTitRJ0NZACFUn3Fs3Rvgc9eN9YSWj4RtqkmGPMPvguWo+SKhlk3IbYjrRBc5WVOdoX8JXb2/+nAGhPCuUZckWVmZe5pMSr4EkNQdYeY8kOXGSjoTOUH34ZdKeS+e399BkBWIiXUejX/Srln0H4KoHnTWgxwNpTsBCgXu8Q==",

三.Startup配置

 

 

 

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/83386
推荐阅读
相关标签
  

闽ICP备14008679号