需求:通过微信公众号菜单跳转到手机端网页,跳转后通过微信授权登录获取微信公众号用户的OpenId(用户关注公众号后,用户在公众号的唯一凭证),通过OpenId和后台数据库用户信息绑定起来并实现一些业务逻辑。
技术框架:网页端使用vue来作为前端框架,.net web api作为后端框架,本文主要记录前后端框架的搭建过程。
1 使用盛派.net SDK搭建后端框架
后端选择.net 6 webp api作为框架,并引入盛派.net SDK开简化开发流程,以下记录后端框架搭建流程。
1.1 创建.net6 webapi 项目
使用vs2022,创建一个.net core webp api项目,
因.net core 3.1 不再支持,所以框架选择.net 6:
创建后启动项目,能在浏览器中访问weatherforecast这个api则说明项目创建成功。
1.2 .net6 webapi 项目中引入Senparc.Weixin SDK
Senparc.Weixin SDK 是由盛派网络(Senparc)团队自主研发的针对微信各模块的开发套件(C# SDK),已全面支持微信公众号、小程序、微信支付、企业号、开放平台、JSSDK、摇一摇周边等模块。Senparc.Weixin SDK 在github上已经获得7K个start ,是最受欢迎的.net 微信开发SDK。既然别人已经造好了轮子,就引入到项目中可以有效的减少开发工作量。
接下来在项目中安装 Senparc.Weixin对应的Nuget 包:Senparc.Weixin.MP(对应公众号)、Senparc.Weixin.MP.Middleware(对应公众号消息中间件),Senparc.Weixin.MP选择的版本是16.12.101-preview2
Senparc.Weixin.MP.Middleware选择的版本是0.3.100.1-preview2,这两个Nuget包都有更新的版本,但是新版的部分API 有变,官方的教程中还是使用旧的版本,所以两个Nuget包的版本和官方教程里保持了一致。
接下来就是在Startup.cs中将Senparc.Weixin中引入,只需修改ConfigureServices 和Configure两个接口,修改后的Startup.cs如下:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Senparc.CO2NET;
using Senparc.CO2NET.AspNet;
using Senparc.Weixin;
using Senparc.Weixin.Entities;
using Senparc.Weixin.MP;
using Senparc.Weixin.RegisterServices;
namespace SenparcWeixin
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddMemoryCache()//使用本地缓存必须添加
.AddSenparcWeixinServices(Configuration);//Senparc.Weixin 注册(必须)
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env,
IOptions<SenparcSetting> senparcSetting, IOptions<SenparcWeixinSetting> senparcWeixinSetting)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
//注册 Senparc.Weixin 及基础库
var registerService = app.UseSenparcGlobal(env, senparcSetting.Value, _ => { }, true)
.UseSenparcWeixin(senparcWeixinSetting.Value, weixinRegister => weixinRegister.RegisterMpAccount(senparcWeixinSetting.Value));
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
启动项目没有报错说明Senparc.Weixin SDK已成功引入到项目。
1.3 微信公众号测试账号申请
通过认证的微信公众号才能调用一些高级接口,开发过程中一般选择申请一个测试账号来开发,测试号可以直接体验和测试公众平台的所有高级接口,测试账号申请地址https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login ,申请成功后会拿到appID等信息。
然后将公众号信息存放到项目中的appsetting.json文件中,程序中Token WeixinAppId WeixinAppSecret这些信息均从appsetting.json这一个地方读取。
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
//CO2NET
"SenparcSetting": {
"IsDebug": true,
"DefaultCacheNamespace": "DefaultCache"
},
//Senparc.Weixin SDK
"SenparcWeixinSetting": {
"IsDebug": true,
"Token": "your Token",
"WeixinAppId": "your WeixinAppId",
"WeixinAppSecret": "your WeixinAppSecret"
}
}
1.4 微信公众号测试账号申请
微信公众平号开发需要配置一个后台服务器的地址,配置的时候微信平台会调用特定的API来验证服务器是否有效,验证的逻辑如下,详细的逻辑可参考官方文档的入门指引 。
配置维修后台服务器时就需要实现这个验证业务逻辑,并将程序发布到自己的服务器。服务器验证业务逻辑实现使用Senparc.Weixin 提供的接口,首先添加一个名为WeixinController的空api Controller。
WeixinController中添加一个get 方法,用以微信后台地址验证,get方法中主要使用了Senparc.Weixin 提供的CheckSignature方法,Token,EncodingAESKey,AppSecret均是从appsetting.json中获取。
using Microsoft.AspNetCore.Mvc;
using Senparc.Weixin;
using Senparc.Weixin.MP;
namespace SenparcWeixin.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class WinxinController : ControllerBase
{
public static readonly string Token = Config.SenparcWeixinSetting.Token;//与微信公众账号后台的Token设置保持一致,区分大小写。
public static readonly string EncodingAESKey = Config.SenparcWeixinSetting.EncodingAESKey;//与微信公众账号后台的EncodingAESKey设置保持一致,区分大小写。
public static readonly string AppId = Config.SenparcWeixinSetting.WeixinAppId;//与微信公众账号后台的AppId设置保持一致,区分大小写。
public static readonly string AppSecret = Config.SenparcWeixinSetting.WeixinAppSecret;
/// <summary>
/// 微信后台验证地址(使用Get),微信后台的“接口配置信息”的Url填写如:http://www.xxxx.com/api/Weixin
/// </summary>
[HttpGet]
[ActionName("Index")]
public ActionResult Get(string signature, string timestamp, string nonce, string echostr)
{
if (CheckSignature.Check(signature, timestamp, nonce, Token))
{
return Content(echostr); //返回随机字符串则表示验证通过
}
else
{
return Content("failed:" + signature + "," + Senparc.Weixin.MP.CheckSignature.GetSignature(timestamp, nonce, Token) + "。如果您在浏览器中看到这条信息,表明此Url可以填入微信后台。");
}
}
}
}
然后修改项目的启动地址,调试的时候选择不依赖IIS,所以修改项目中launchsettings.json中的SenparcWeixin配置,SenparcWeixin是项目的名称,将启动地址修改为http://192.168.1.4:9000。
"SenparcWeixin": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "weatherforecast",
"applicationUrl": "http://192.168.1.4:9000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
调试启动的时候选择SenparcWeixin。
微信公众号接口服务器不能配置成http://192.168.1.4这样的数据域名,需要使用natapp将http://192.168.1.4:9000这个地址进行内网穿透,如何内网穿透可参考公众号开发(1) —— natapp 内网穿透,内网穿透后将微信公众号接口后台配置成natapp中的域名 ,如:http://xx.xx.xx/api/Weixin,提交后显示配置成功。
至此微信公众号服务接入部分工作结束。
2 vue微信公众号授权登录
2.1 vue授权登录基本方案
要获取用户的信息,就必须让用户进行授权,授权的时候要先调用微信的接口获取code,再用code去后端换取openid,微信公众号网页授权详细内容见官方网页授权文档。vue项目中授权放在主入口,建立一个Login.vue,Login.vue中内容如下:
<template>
<div>
<h2>我是Login</h2>
</div>
</template>
<script>
import axios from 'axios'
export default {
data () {
return {
appid: "your appid",
openid: "",
};
},
created () {
// 从 window.location.href 中截取 code
let code = this.getUrlCode().code;
if (code) {
console.log('code', code);
//code已存在,使用code通过后端获取Openid
let url = 'http://192.168.1.4/api/WeiXin/GetOpenId'
axios.get(url, {
params: {
code: code,
}
})
.then((res) => {
console.log('openid', res.data);
})
.catch((error) => {
alert(error);
});
} else {
//code不存在,调用维修接口获取code
this.getCodeApi();
}
},
methods: {
getCodeApi () {
// 重定向地址重定到当前页面,在路径获取 code
let urlNow = 'http://192.168.1.4';
let url =
"https://open.weixin.qq.com/connect/oauth2/authorize?appid=" +
this.appid +
"&redirect_uri=" +
urlNow +
"&response_type=code&scope=snsapi_userinfo#wechat_redirect";
window.location.href = url;
},
getUrlCode () {
// 截取url中的code方法
var url = location.search;
var theRequest = new Object();
if (url.indexOf("?") != -1) {
var str = url.substr(1);
var strs = str.split("&");
for (var i = 0; i < strs.length; i++) {
theRequest[strs[i].split("=")[0]] = strs[i].split("=")[1];
}
}
console.log(theRequest);
return theRequest;
},
},
};
</script>
<style scoped>
</style>
基本的逻辑是前端页面用户同意授权,获取code,然后将code传回后端,通过 code 换取网页授权access_token。注意,这里通过 code 换取的是一个特殊的网页授权access_token,与基础支持中的access_token(该access_token用于调用其他接口)不同,access_token请求成功后返回的数据如下:
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE",
"is_snapshotuser": 1,
"unionid": "UNIONID"
}
在后台的WeixinController中添加一个GetOpenId 的api,通过调用SDK中提供的GetAccessToken实现用code 换取access_token的流程,access_token获取成功后给前端返回openid,后端GetOpenId 代码如下:
/// <summary>
/// 根据code获取维修公众号用户openid
/// </summary>
[HttpGet]
[Route("GetOpenId/")]
public ActionResult GetOpenId(string code)
{
OAuthAccessTokenResult result = null;
result = OAuthApi.GetAccessToken(AppId, AppSecret, code);
return Content(result.openid);
}
前端对应的请求openId的代码:
//code已存在,使用code通过后端获取Openid
let url = 'http://192.168.1.4/api/WeiXin/GetOpenId'
axios.get(url, {
params: {
code: code,
}
})
.then((res) => {
console.log('openid', res.data);
})
.catch((error) => {
alert(error);
});
2.2 授权回调页面域名设置
需要注意的是微信公众号网页授权成功后会跳转到授权回调页面域名,所以测试前必须在公众号管理页面设置授权回调页面域名
授权回调页面是可以填写成数字域名的,为了调试方便,先填写成192.168.1.4这个数字域名(注意:授权回调域名只支持80 和443端口)。
2.3 反向代理解决授权回调页面域名端口问题以及后端api跨域问题
前端vue项目的启动地址是 http://192.168.1.4:8081/,原本的逻辑是授权登录后拿到code后再跳转回 http://192.168.1.4:8081/这个地址并获取openId,但是授权回调页面域名只支持80端口,上面的配置默认会 http://192.168.1.4:80/这个域名,与预期的逻辑不符。
这个时候就需要用到nginx反向代理,反向代理(Reverse Proxy)方式是指以代理服务器来接受Internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给Internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器。对应本案例,用户访问http://192.168.1.4:80/这个域名时通过nginx将请求转到 http://192.168.1.4:8081/,用户实际访问的还是 http://192.168.1.4:8081/这个域名,这样就解决了授权回调页面域名端口问题。
另外,后端webp api的地址是 http://192.168.1.4:9000/,端口不同,前端调用后端api属于跨域访问。nginx支持带路径的反向代理,可以通过配置将http://192.168.1.4:80/api/ 反向代理到http://192.168.1.4:9000/api/,这样就解决了前端跨域的问题。
windows平台使用nginx反向代理很简单:
(1)从官网下载对应版本的nginx并解压。
(2)更改nginx\conf\nginx.conf文件,反向代理主要server节点,修改内容如下:
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name 192.168.1.4;
location / {
proxy_pass http://192.168.1.4:8081/;
}
location ^~ /api/ {
proxy_pass http://192.168.1.4:9000/api/;
}
}
}
监听的是192.168.1.4:80,如果用户访问http://192.168.1.4:80/,会被转发到http://192.168.1.4:8081/域名;如果访问http://192.168.1.4:80/api/这个域名,会被转发到http://192.168.1.4:9000/api/域名。
(3)双击nginx.exe启动。
2.4 使用微信开发者工具测试授权登录
nginx反向代理启动后就可以测试授权登功能了,测试的时候需要在微信web 开发者工具中调试。web 开发者工具是一个桌面应用,通过模拟微信客户端的表现,使得开发者可以使用这个工具方便地在 PC上进行开发和调试工作。vue项目的调试地址为http://192.168.1.4:8081/,打开微信开发者工具,切换到公众号网页调试,地址栏输入http://192.168.1.4:8081/,即可进入公众号授权登录。
授权登录登录成功后通过console log中看到获取到的openId后台管理界面的openId一致。
公众号与后台看到的openId。
至此,微信公众号网页开发框架搭建完毕。
3 参考文章
1 https://sdk.weixin.senparc.com/
2 https://www.cnblogs.com/szw/p/wecaht-minimization-example.html