当前位置:网站首页>Wechat - developed by wechat official account Net core access
Wechat - developed by wechat official account Net core access
2022-07-03 02:31:00 【Python's path to immortality】
.net There seem to be few examples of wechat official account development , Take a note here
First , We need wechat to access our project , So you either need a server that can deploy the project and connect to the public network , Or you can forward the request to our project through port forwarding , All in all , It is to enable wechat server to access our project .
in addition , You need to pay attention to , The address of wechat callback notification currently only supports 80 Port and 443 port , So in general , We all need to make a virtual path
Not much else , The specific configuration can be accessed in the development document of wechat official account : Access overview | Wechat open documents
Many pits will be encountered in the access process , what Url Overtime ,Token Validation errors, etc , Anyway, there are some things that cannot be prevented , When we access the development , It is found that we use plaintext transmission , Of course not , Change it into ciphertext and use AES encryption , Anyway, I don't know how many pits I encountered
The code I accessed is posted below , Copy it , You can use it with a little modification , It can directly verify the access , Support plaintext and ciphertext transmission :
An encryption and decryption auxiliary class :
EncryptHelper.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
namespace DemoApi
{
public sealed class EncryptHelper
{
private EncryptHelper() { }
/// <summary>
/// Md5 encryption
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static string Md5Encrypt(string text)
{
//MD5 encryption
var md5 = MD5.Create();
var bs = md5.ComputeHash(Encoding.UTF8.GetBytes(text));
var sb = new StringBuilder();
foreach (byte b in bs)
{
sb.Append(b.ToString("x2"));
}
// All characters are capitalized
return sb.ToString().ToUpper();
}
/// <summary>
/// HMAC-SHA1 encryption algorithm
/// </summary>
/// <param name="str"> Encrypted string </param>
/// <returns></returns>
public static string Sha1Encrypt(string str)
{
var sha1 = SHA1.Create();
var hash = sha1.ComputeHash(Encoding.Default.GetBytes(str));
//return BitConverter.ToString(hash).Replace("-", "");
string byte2String = null;
for (int i = 0; i < hash.Length; i++)
{
byte2String += hash[i].ToString("x2");
}
return byte2String;
}
/// <summary>
/// AES encryption
/// </summary>
/// <param name="text"> Encrypted characters </param>
/// <param name="encodingAESKey"> Encrypted password </param>
/// <param name="appid">appId</param>
/// <returns></returns>
public static string AESEncrypt(string text, string encodingAESKey, string appid)
{
if (string.IsNullOrEmpty(text))
{
return text;
}
byte[] key;
key = Convert.FromBase64String(encodingAESKey + "=");
byte[] iv = new byte[16];
Array.Copy(key, iv, 16);
#region Generate random values
string codeSerial = "2,3,4,5,6,7,a,c,d,e,f,h,i,j,k,m,n,p,r,s,t,A,C,D,E,F,G,H,J,K,M,N,P,Q,R,S,U,V,W,X,Y,Z";
string[] arr = codeSerial.Split(',');
string code = "";
int randValue = -1;
Random rand = new Random(unchecked((int)DateTime.Now.Ticks));
for (int i = 0; i < 16; i++)
{
randValue = rand.Next(0, arr.Length - 1);
code += arr[randValue];
}
#endregion
byte[] bRand = Encoding.UTF8.GetBytes(code);
byte[] bAppid = Encoding.UTF8.GetBytes(appid);
byte[] btmpMsg = Encoding.UTF8.GetBytes(text);
int outval = 0, inval = btmpMsg.Length;
for (int i = 0; i < 4; i++)
outval = (outval << 8) + ((inval >> (i * 8)) & 255);
byte[] bMsgLen = BitConverter.GetBytes(outval);
byte[] bMsg = new byte[bRand.Length + bMsgLen.Length + bAppid.Length + btmpMsg.Length];
Array.Copy(bRand, bMsg, bRand.Length);
Array.Copy(bMsgLen, 0, bMsg, bRand.Length, bMsgLen.Length);
Array.Copy(btmpMsg, 0, bMsg, bRand.Length + bMsgLen.Length, btmpMsg.Length);
Array.Copy(bAppid, 0, bMsg, bRand.Length + bMsgLen.Length + btmpMsg.Length, bAppid.Length);
var aes = new RijndaelManaged();
// The size of the secret key , In bits
aes.KeySize = 256;
// Supported block sizes
aes.BlockSize = 128;
// Fill mode
//aes.Padding = PaddingMode.PKCS7;
aes.Padding = PaddingMode.None;
aes.Mode = CipherMode.CBC;
aes.Key = key;
aes.IV = iv;
var encrypt = aes.CreateEncryptor(aes.Key, aes.IV);
byte[] xBuff = null;
byte[] msg = new byte[bMsg.Length + 32 - bMsg.Length % 32];
Array.Copy(bMsg, msg, bMsg.Length);
#region Do it yourself PKCS7 Fill a , You can't bring it yourself with the system , Wechat encryption should use this
int block_size = 32;
// Calculate the number of digits to be filled
int amount_to_pad = block_size - (bMsg.Length % block_size);
if (amount_to_pad == 0)
{
amount_to_pad = block_size;
}
// The character used to get the complement
char pad_chr = (char)(byte)(amount_to_pad & 0xFF);
string tmp = "";
for (int index = 0; index < amount_to_pad; index++)
{
tmp += pad_chr;
}
byte[] pad = Encoding.UTF8.GetBytes(tmp);
Array.Copy(pad, 0, msg, bMsg.Length, pad.Length);
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write))
{
cs.Write(msg, 0, msg.Length);
}
xBuff = ms.ToArray();
}
#endregion
#region Annotation is also a method , The effect is the same , Wechat encryption cannot use this !!!!
//ICryptoTransform transform = aes.CreateEncryptor();
//xBuff = transform.TransformFinalBlock(msg, 0, msg.Length);
#endregion
string output = Convert.ToBase64String(xBuff);
return output;
}
/// <summary>
/// AES Decrypt
/// </summary>
/// <param name="encryptText"> Ciphertext </param>
/// <param name="encodingAESKey"> Secret key </param>
/// <param name="appid"></param>
/// <returns></returns>
public static string AESDecrypt(string encryptText, string encodingAESKey, out string appid)
{
if (string.IsNullOrEmpty(encryptText))
{
appid = "";
return encryptText;
}
byte[] key;
key = Convert.FromBase64String(encodingAESKey + "=");
byte[] iv = new byte[16];
Array.Copy(key, iv, 16);
byte[] btmpMsg = null;
RijndaelManaged aes = new RijndaelManaged();
aes.KeySize = 256;
aes.BlockSize = 128;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.None;
aes.Key = key;
aes.IV = iv;
var decrypt = aes.CreateDecryptor(aes.Key, aes.IV);
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Write))
{
byte[] xXml = Convert.FromBase64String(encryptText);
byte[] msg = new byte[xXml.Length + 32 - xXml.Length % 32];
Array.Copy(xXml, msg, xXml.Length);
cs.Write(xXml, 0, xXml.Length);
}
var decrypted = ms.ToArray();
int pad = (int)decrypted[decrypted.Length - 1];
if (pad < 1 || pad > 32)
{
pad = 0;
}
btmpMsg = new byte[decrypted.Length - pad];
Array.Copy(decrypted, 0, btmpMsg, 0, decrypted.Length - pad);
}
int len = BitConverter.ToInt32(btmpMsg, 16);
len = IPAddress.NetworkToHostOrder(len);
byte[] bMsg = new byte[len];
byte[] bAppid = new byte[btmpMsg.Length - 20 - len];
Array.Copy(btmpMsg, 20, bMsg, 0, len);
Array.Copy(btmpMsg, 20 + len, bAppid, 0, btmpMsg.Length - 20 - len);
string oriMsg = Encoding.UTF8.GetString(bMsg);
appid = Encoding.UTF8.GetString(bAppid);
return oriMsg;
}
/// <summary>
/// AES Decrypt
/// </summary>
/// <param name="encryptText"> Ciphertext </param>
/// <param name="encodingAESKey"> Secret key </param>
/// <param name="appid"></param>
/// <returns></returns>
public static string AESDecrypt(string encryptText, string encodingAESKey)
{
return AESDecrypt(encryptText, encodingAESKey, out _);
}
}
public class CharSort : IComparer
{
public int Compare(object left, object right)
{
string sLeft = left as string;
string sRight = right as string;
int leftLength = sLeft.Length;
int rightLength = sRight.Length;
int index = 0;
while (index < leftLength && index < rightLength)
{
if (sLeft[index] < sRight[index])
return -1;
else if (sLeft[index] > sRight[index])
return 1;
else
index++;
}
return leftLength - rightLength;
}
}
}
Core code of access interface :
WxController.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web;
using System.Xml.Linq;
using Microsoft.AspNetCore.Mvc;
namespace DemoApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class WxController : ControllerBase
{
/// <summary>
/// Generate signature
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
[NonAction]
private string MakeSign(params string[] args)
{
// Dictionary sort
Array.Sort(args);
string tmpStr = string.Join("", args);
// Character encryption
var sha1 = EncryptHelper.Sha1Encrypt(tmpStr);
return sha1;
}
/// <summary>
/// Generate message signature
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
[NonAction]
private string MakeMsgSign(params string[] args)
{
// Dictionary sort
Array.Sort(args, new CharSort());
string tmpStr = string.Join("", args);
// Character encryption
var sha1 = EncryptHelper.Sha1Encrypt(tmpStr);
return sha1;
}
/// <summary>
/// Wechat callback unified interface
/// </summary>
/// <returns></returns>
[HttpGet, HttpPost]
public string Service()
{
// Get the data in the configuration file
var token = "";
var encodingAESKey = "";
var appId = "";
bool isGet = string.Equals(HttpContext.Request.Method, HttpMethod.Get.Method, StringComparison.OrdinalIgnoreCase);
bool isPost = string.Equals(HttpContext.Request.Method, HttpMethod.Post.Method, StringComparison.OrdinalIgnoreCase);
if (!isGet && !isPost)
{
return "";
}
bool isEncrypt = false;
try
{
var query = HttpContext.Request.QueryString.ToString();
string msg_signature = "", nonce = "", timestamp = "", encrypt_type = "", signature = "", echostr = "";
if (!string.IsNullOrEmpty(query))// Need to verify signature
{
var collection = HttpUtility.ParseQueryString(query);
msg_signature = collection["msg_signature"]?.Trim();
nonce = collection["nonce"]?.Trim();
timestamp = collection["timestamp"]?.Trim();
encrypt_type = collection["encrypt_type"]?.Trim();
signature = collection["signature"]?.Trim();
echostr = collection["echostr"]?.Trim();
if (!string.IsNullOrEmpty(encrypt_type))// Yes, encryption is used
{
if (!string.Equals(encrypt_type, "aes", StringComparison.OrdinalIgnoreCase))// Only support AES encryption
{
return "";
}
isEncrypt = true;
}
}
// Verify the signature first
if (!string.IsNullOrEmpty(signature))
{
// Character encryption
var sha1 = MakeSign(nonce, timestamp, token);
if (!sha1.Equals(signature, StringComparison.OrdinalIgnoreCase))// Verification failed
{
return "";
}
if (isGet)// whether Get request , If true, Then it is considered to be modifying the server callback configuration information
{
return echostr;
}
}
else
{
return "";// No signature , Request direct return
}
var body = new StreamReader(HttpContext.Request.Body).ReadToEnd();
if (isEncrypt)
{
XDocument doc = XDocument.Parse(body);
var encrypt = doc.Element("xml").Element("Encrypt");
// Verify message signature
if (!string.IsNullOrEmpty(msg_signature))
{
// Message encryption
var sha1 = MakeMsgSign(nonce, timestamp, encrypt.Value, token);
if (!sha1.Equals(msg_signature, StringComparison.OrdinalIgnoreCase))// Verification failed
{
return "";
}
}
body = EncryptHelper.AESDecrypt(encrypt.Value, encodingAESKey);// Decrypt
}
if (!string.IsNullOrEmpty(body))
{
//
// Here according to body Medium MsgType and Even To distinguish messages , Then deal with different business logic
//
//
//result It is the result to be returned after the above logical processing , Such as returning text messages :
var result = @"<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[ Hello ]]></Content>
</xml>";
if (!string.IsNullOrEmpty(result))
{
if (isEncrypt)
{
result = EncryptHelper.AESEncrypt(result, encodingAESKey, appId);
var _msg_signature = MakeMsgSign(nonce, timestamp, result, token);
result = [email protected]"<xml>
<Encrypt><![CDATA[{result}]]></Encrypt>
<MsgSignature>{_msg_signature}</MsgSignature>
<TimeStamp>{timestamp}</TimeStamp>
<Nonce>{nonce}</Nonce>
</xml>";
}
return result;
}
// If our processing logic here takes a long time , You can return null here first (""), Then use asynchrony to process business logic ,
// After asynchronous processing , Call the wechat customer service message interface to notify the wechat server
}
}
catch (Exception ex)
{
// Record exception log
}
return "";
}
}
}
If you just connect , Just copy the interface code above , Revise it token,encodingAESKey,appId Value , Then use the callback of access Url:http://XXX.XXXX.com/api/Wx That's all right.
If you want to process the messages notified by wechat , You can process the business logic according to the above interface , But here's the thing , Wechat callback interface will only wait 5 second ,5 Seconds later , And repeat 3 Time , So if the business logic processing time here is relatively long , Asynchronous is recommended , After asynchronous processing , Use the customer service message interface to notify the results
In addition, during the development process , You can use wechat public platform interface debugging tool to debug : Wechat public platform interface debugging tool
A little bit of attention , General , When we write interfaces , To ensure that the data returned by the interface has a certain format , So I will write some filters , Wrap the interface , For example, we will package the results into the following structure :
{
"result": "success",
"success": true,
"error": null,
"message": ""
}
But the interface called by wechat cannot be wrapped , You need to return to the format specified by wechat , Remember this !!! Otherwise it will return to Token Error of validation failure !
边栏推荐
- Mathematical statistics -- Sampling and sampling distribution
- 【教程】chrome關閉跨域策略cors、samesite,跨域帶上cookie
- GBase 8c系统表-pg_aggregate
- [translation] the background project has joined the CNCF incubator
- oauth2.0鉴权,登录访问 “/oauth/token”,请求头Authorization(basicToken)如何取值???
- 4. Classes and objects
- GBase 8c系统表-pg_amop
- 5. File operation
- cvpr2022去雨去雾
- 【翻译】具有集中控制平面的现代应用负载平衡
猜你喜欢
[Flutter] dart: class; abstract class; factory; Class, abstract class, factory constructor
Recommendation letter of "listing situation" -- courage is the most valuable
[translation] the background project has joined the CNCF incubator
内存池(内核角度理解new开辟空间的过程)
【ROS进阶篇】第六讲 ROS中的录制与回放(rosbag)
[fluent] JSON model conversion (JSON serialization tool | JSON manual serialization | writing dart model classes according to JSON | online automatic conversion of dart classes according to JSON)
Tongda OA homepage portal workbench
random shuffle注意
easyPOI
Basic operation of binary tree (C language version)
随机推荐
Awk from introduction to earth (0) overview of awk
How to change the panet layer in yolov5 to bifpn
require.context
Gbase 8C create user / role example 2
[shutter] bottom navigation bar page frame (bottomnavigationbar bottom navigation bar | pageview sliding page | bottom navigation and sliding page associated operation)
[Hcia]No.15 Vlan间通信
Memory pool (understand the process of new developing space from the perspective of kernel)
What does "where 1=1" mean
[hcia]no.15 communication between VLANs
GBase 8c系统表-pg_am
[fluent] JSON model conversion (JSON serialization tool | JSON manual serialization | writing dart model classes according to JSON | online automatic conversion of dart classes according to JSON)
javeScript 0.1 + 0.2 == 0.3的问题
Return a tree structure data
Gbase 8C system table PG_ cast
Servlet中数据传到JSP页面使用el表达式${}无法显示问题
【CodeForces】CF1338A - Powered Addition【二进制】
Producer consumer model based on thread pool (including blocking queue)
CFdiv2-Fixed Point Guessing-(區間答案二分)
GBase 8c 触发器(一)
Gbase 8C system table PG_ database