三方web应用对接公众号流程
1.申请申请微信公众号 测试平台
填写对应信息,我们在测试平台单数url是要公网的,这个是时候可以内网穿透 我们的服务推荐natapp 不推荐ngrok
填写完对于信息之后,我们就可以体验公众号提供的接口了!!
2.后端要提供给公众号接口验证
这里我以c#代码为例子
public string VerifyWxConfig(string signature, string timestamp, string nonce, string echostr)
{
if (CheckSignature(signature, timestamp, nonce))
{
return echostr;
}
return string.Empty;
}
private bool CheckSignature(string signature, string timestamp, string nonce)
{
// 将token、timestamp、nonce三个参数进行字典序排序
string[] arr = new[] {Config.MyConfig.token, timestamp, nonce }.OrderBy(x => x).ToArray();
// 将三个参数字符串拼接成一个字符串
string tempStr = string.Join("", arr);
// 进行sha1加密
using (var sha1 = SHA1.Create())
{
var hashBytes = sha1.ComputeHash(Encoding.UTF8.GetBytes(tempStr));
var tempSignature = BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
// 返回验证结果
return tempSignature == signature;
}
}
参数配置项 建议定义一个config文件
3.条用公众号接口
前端请求
let origin = window.location.origin;
let config = null;
(async function init() {
// 1. 加载配置
await getConfig();
// 2. 执行后续逻辑
await getInfo();
// 3. 其他初始化操作
console.log('初始化完成', config);
})();
let getInfo = () => {
// 是否为回调页面
let paramsObj = getUrlParams()
console.log('paramsObj', paramsObj);
// 防止刷新页面时,数据丢失,故需要回填数据
let name = window.localStorage.getItem("nickname")
let openid = window.localStorage.getItem("openid")
if (paramsObj && paramsObj.code) {
// 如果有code参数,说明是微信回调,需要获取用户信息
getUserInfoByCode(paramsObj.code)
} else if (name && openid) {
// 如果本地有用户信息,直接使用
config.openid = openid;
// 获取我的其他资料,图片等等
getUserInfo(openid)
} else {
// 初次页面,需要调起授权页面
let backUrl = encodeURIComponent(config.redirectUrl)
let url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${config.appId}&redirect_uri=${backUrl}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect`
window.location.href = url;
}
}
// 获取配置信息
async function getConfig() {
try {
const response = await axios.get(`${origin}/open/getConfig`, {
headers: {
'Cache-Control': 'no-cache'
},
timeout: 5000
});
console.log('配置获取成功:', response.data);
config = response.data;
return config;
} catch (error) {
console.error('配置获取失败:', error);
throw new Error('系统配置加载失败');
}
}
// 获取用户数据
async function getUserInfo(openid) {
try {
const response = await axios.post(`${origin}/open/getUserInfoByOpenid`,
{ openid },
{
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${config.token}`
}
}
);
console.log('用户信息获取成功:', response.data);
return response.data;
} catch (error) {
console.error('用户信息获取失败:', error);
throw new Error('用户信息获取失败');
}
}
// 通过code获取用户信息
async function getUserInfoByCode(code) {
try {
const response = await axios.get(`${origin}/open/getUserInfo?code=${code}`);
console.log('通过code获取用户信息成功:', response.data);
// 保存用户信息到本地存储
window.localStorage.setItem('nickname', response.data.nickname);
window.localStorage.setItem('openid', response.data.openid);
window.localStorage.setItem('aiUrl', response.data.aiUrl);
// 设置到config中
config.openid = response.data.openid;
// 获取其他用户资料
await getUserInfo(response.data.openid);
// 清除URL中的code参数,防止刷新时重复获取
window.history.replaceState({}, document.title, window.location.pathname);
// 显示提示信息
showToast(response.data.aiUrl ? `欢迎回来,${response.data.nickname}` : '您暂无权限使用AI助手,请申请权限');
// 直接使用返回的aiUrl加载聊天机器人
loadChatBot(response.data.aiUrl);
} catch (error) {
console.error('通过code获取用户信息失败:', error);
throw new Error('获取用户信息失败');
}
}
后端代码
/// <summary>
/// 获取微信授权URL
/// </summary>
/// <param name="state">状态参数</param>
/// <returns>授权URL</returns>
public string getAuthorizeUrl(string state = "STATE")
{
string redirectUri = System.Web.HttpUtility.UrlEncode(Config.MyConfig.redirectUrl);
return $"https://open.weixin.qq.com/connect/oauth2/authorize?appid={Config.MyConfig.appId}&redirect_uri={redirectUri}&response_type=code&scope=snsapi_userinfo&state={state}#wechat_redirect";
}
/// <summary>
/// 获取配置信息
/// </summary>
/// <returns>配置信息</returns>
public string getConfig()
{
return JsonSerializer.Serialize(Config.MyConfig);
}
/// <summary>
/// 获取用户信息
/// </summary>
/// <param name="code">微信授权code</param>
/// <returns>用户信息</returns>
public async Task<WxUserInfo> GetUserInfoAsync(string code)
{
try
{
// 1. 通过code获取access_token和openid
string url = $"https://api.weixin.qq.com/sns/oauth2/access_token?appid={Config.MyConfig.appId}&secret={Config.MyConfig.appSecret}&code={code}&grant_type=authorization_code";
var response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadAsStringAsync();
var tokenResponse = JsonSerializer.Deserialize<WxOAuthResponse>(result);
Console.WriteLine($"获取access_token成功:{tokenResponse?.AccessToken}");
if (tokenResponse?.ErrCode != 0)
{
Console.WriteLine($"获取access_token失败:{tokenResponse?.ErrMsg}");
return null;
}
// 2. 通过access_token和openid获取用户信息
string userInfoUrl = $"https://api.weixin.qq.com/sns/userinfo?access_token={tokenResponse.AccessToken}&openid={tokenResponse.OpenId}&lang=zh_CN";
var userInfoResponse = await _httpClient.GetAsync(userInfoUrl);
userInfoResponse.EnsureSuccessStatusCode();
var userInfoResult = await userInfoResponse.Content.ReadAsStringAsync();
var userInfo = JsonSerializer.Deserialize<WxUserInfo>(userInfoResult);
if (userInfo?.ErrCode != 0)
{
Console.WriteLine($"获取用户信息失败:{userInfo?.ErrMsg}");
return null;
}
Console.WriteLine($"获取用户信息成功:{userInfo}");
if (Config.MyConfig.idList.Contains(userInfo?.OpenId))
{
byte[] plainTextBytes = Encoding.UTF8.GetBytes(Config.MyConfig.AIurl);
string base64String = Convert.ToBase64String(plainTextBytes);
userInfo.AIurl = base64String;
}
return userInfo;
}
catch (Exception ex)
{
Console.WriteLine($"获取用户信息异常:{ex.Message}");
return null;
}
}
/// <summary>
/// 通过openid获取用户信息
/// </summary>
public async Task<WxUserInfo> GetUserInfoByOpenidAsync(string openid)
{
try
{
// 1. 获取全局access_token
string accessToken = await GetAccessTokenAsync();
if (string.IsNullOrEmpty(accessToken))
{
return null;
}
// 2. 通过access_token和openid获取用户信息
string userInfoUrl = $"https://api.weixin.qq.com/cgi-bin/user/info?access_token={accessToken}&openid={openid}&lang=zh_CN";
var userInfoResponse = await _httpClient.GetAsync(userInfoUrl);
userInfoResponse.EnsureSuccessStatusCode();
var userInfoResult = await userInfoResponse.Content.ReadAsStringAsync();
var userInfo = JsonSerializer.Deserialize<WxUserInfo>(userInfoResult);
if (userInfo?.ErrCode != 0)
{
Console.WriteLine($"获取用户信息失败:{userInfo?.ErrMsg}");
return null;
}
Console.WriteLine($"获取用户信息成功:{userInfo}");
if (Config.MyConfig.idList.Contains(userInfo?.OpenId))
{
byte[] plainTextBytes = Encoding.UTF8.GetBytes(Config.MyConfig.AIurl);
string base64String = Convert.ToBase64String(plainTextBytes);
userInfo.AIurl = base64String;
}
return userInfo;
}
catch (Exception ex)
{
Console.WriteLine($"获取用户信息异常:{ex.Message}");
return null;
}
}
/// <summary>
/// 获取全局access_token
/// </summary>
private async Task<string> GetAccessTokenAsync()
{
try
{
// 如果access_token未过期,直接返回
if (!string.IsNullOrEmpty(_accessToken) && DateTime.Now < _tokenExpireTime)
{
return _accessToken;
}
// 获取新的access_token
string url = $"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={Config.MyConfig.appId}&secret={Config.MyConfig.appSecret}";
var response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadAsStringAsync();
var tokenResponse = JsonSerializer.Deserialize<WxTokenResponse>(result);
if (tokenResponse?.ErrCode != 0)
{
Console.WriteLine($"获取access_token失败:{tokenResponse?.ErrMsg}");
return null;
}
_accessToken = tokenResponse.AccessToken;
_tokenExpireTime = DateTime.Now.AddSeconds(tokenResponse.ExpiresIn - 10); // 提前10秒过期
return _accessToken;
}
catch (Exception ex)
{
Console.WriteLine($"获取access_token异常:{ex.Message}");
return null;
}
}
调试是否获取到我们需要下载微信开发工具 这里更换开发模式,选择公众号网页
测试完之后 部署上线,我要更改config设置 换成正式环境的域名
查看微信接口权限
获取用户信息 推送消息等接口都需要微信认证,也是在设置与开发 下面,微信认证需要企业资质,费用大概300/年。