前言
作为个人开发者或小团队,你是否也遇到过这样的困境:想给项目加个微信扫码登录功能,结果被官方要求的「企业资质认证」「300 元年费」「域名备案」拦住去路?流程繁琐不说,成本也让很多小型项目望而却步。
登录又是所有系统不可或缺的,为解决这个小麻烦,我开发了一套免企业资质、免年费、免备案的轻量版微信扫码登录模块,无需复杂配置,Web 和 WinForm 项目都能快速集成。目前已稳定运行,今天把完整方案分享给大家,附详细集成教程和真实可复用的代码~
一、核心亮点(为什么选它?)
- 零门槛接入:无需企业资质、不用交 300 元年费、不用备案域名,个人开发者 / 小团队直接用
- 双端支持:同时适配 Web 项目(Vue/React/ 原生 HTML)和 WinForm 桌面应用
- 集成超简单:Web 端用 iframe 嵌套 + 事件监听,WinForm 直接复用现成代码,5 分钟搞定
- 稳定可靠:二维码有效期 60 秒自动刷新,扫码结果实时响应,无额外复杂依赖
- 完全免费:个人 / 商业使用均无费用,后续会持续迭代功能
- 原生适配:WinForm 代码基于 CefSharp开发,兼容主流桌面应用框架
二、功能演示
1. 可直接集成的二维码
在线演示地址:ScanQrcode
(注:实际使用时可直接通过接口获取,无需自己生成)
2. 扫码效果
用户用微信扫码后,模块实时返回用户 OpenId(唯一标识),无需授权登录,快速完成身份校验,WinForm 端自动更新状态并跳转主页。
WinForm 端登录页
通过pictureBox控件显示二维码,60 秒内未扫码自动刷新,扫码成功后自动存储 OpenId 并关闭登录页,进入系统主页。
三、详细集成教程
(一)Web 项目集成(原生 HTML/Vue/React 通用)
1. 核心原理
用 iframe 嵌套官方二维码,通过window.postMessage监听扫码结果回调,拿到 OpenId 后完成登录逻辑。
2. 完整代码示例(原生 HTML)
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>微信扫码登录(免资质版)</title>
<style>
/* 样式优化,适配不同屏幕 */
body {
margin: 0;
padding: 0;
height: 100vh;
font-family: "Microsoft YaHei", Arial, sans-serif;
background: #f5f6fa;
display: flex;
align-items: center;
justify-content: center;
}
.container {
display: flex;
width: 900px;
height: 400px;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1);
border-radius: 16px;
background: #fff;
overflow: hidden;
}
.left {
flex: 1;
background: #4f8cff;
color: #fff;
display: flex;
flex-direction: column;
justify-content: center;
padding: 0 60px;
}
.left h1 {
font-size: 28px;
margin-bottom: 20px;
}
.left p {
font-size: 16px;
line-height: 1.8;
opacity: 0.9;
}
.right {
flex: 1;
background: #f0f4ff;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 32px;
}
.qr-container {
width: 180px;
height: 180px;
background: #fff;
border: 1px solid #ddd;
border-radius: 8px;
overflow: hidden;
position: relative;
margin-bottom: 20px;
}
/* iframe缩放适配 */
.qr-container iframe {
width: 430px;
height: 430px;
border: none;
transform: scale(0.4186);
transform-origin: top left;
display: block;
}
.right span {
font-size: 15px;
color: #333;
}
/* 响应式适配手机 */
@media (max-width: 1000px) {
.container {
flex-direction: column;
width: 98vw;
height: auto;
margin-top: 20px;
}
.left, .right {
width: 100%;
height: 250px;
padding: 20px;
}
.qr-container {
width: 150px;
height: 150px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="left">
<h1>欢迎使用本系统</h1>
<p>高效、安全、便捷的管理平台<br>助力您的业务快速成长</p>
</div>
<div class="right">
<div class="qr-container">
<!-- 嵌套二维码iframe -->
<iframe src="http://106.14.223.151:18007/qrcode" frameborder="0" scrolling="no"></iframe>
</div>
<span>请使用微信扫码登录</span>
</div>
</div>
<script>
// 监听iframe回调的扫码结果
window.addEventListener('message', function(event) {
// 安全校验:仅接收指定域名的回调(必加!防止恶意请求)
if (event.origin !== 'http://106.14.223.151:18007') return;
try {
// 解析回调数据(包含用户OpenId)
const scanData = JSON.parse(event.data);
console.log('扫码成功,用户信息:', scanData.value);
// 这里可扩展自己的业务逻辑:跳转主页/注册页、保存用户信息等
alert(`登录成功!用户ID:${scanData.value.openId}`);
// window.location.href = `/home?openId=${scanData.value.openId}`;
} catch (e) {
console.error('解析扫码结果失败:', e);
}
});
</script>
</body>
</html>
3. 集成步骤(3 步搞定)
- 复制上面的 HTML 代码,直接嵌入自己的登录页;
- 按需修改样式(颜色、尺寸、文案),适配项目 UI;
- 在
message事件回调中,添加自己的登录 / 注册逻辑(如通过 OpenId 查询用户、创建新用户等)。
(二)WinForm 项目集成(真实可复用代码)
1. 核心依赖
需提前通过 NuGet 安装以下包:
Newtonsoft.Json(解析 JSON 响应)CefSharp.WinForms(桌面端浏览器内核支持)System.Net.Http(HTTP 请求工具)
2. 核心接口说明
| 接口名称 | 接口地址 | 说明 |
|---|---|---|
| 获取二维码 | http://106.14.223.151:18003/api/WeChat/loginCode | 返回二维码图片字节流,直接用于显示 |
| 轮询扫码状态 | http://106.14.223.151:18003/api/WeChat/LoginState | 2 秒轮询一次,返回 3 种状态 |
| 刷新二维码(超时) | http://106.14.223.151:18003/api/WeChat/loginCode?sign=timeout | 二维码过期后重新获取 |
| 扫码成功标识 | http://106.14.223.151:18003/api/WeChat/loginCode?sign=success | 扫码成功后更新图片标识 |
3. 轮询状态说明
- 二维码待扫码(正常状态):
{
"statusCode": 200,
"remark": "",
"message": "",
"value": {"reqId": "55db77b440c9839bada7f692bdfd819c"}
}
2.扫码成功(返回 OpenId):
{
"statusCode": 200,
"remark": "",
"message": "",
"value": {"openId": "wxoid_fjhxf7cau76"} // 用户唯一标识
}
3.二维码失效 / 未使用:
{
"statusCode": 300,
"remark": "",
"message": "",
"remark": "Operation failed"
}
- 完整可复用代码(直接复制到项目中)
using CefSharp;
using CefSharp.WinForms;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using System.Windows.Forms;
namespace zhuang.codeprint.winform.Frms
{
public partial class frmLogin : Form
{
private HttpClient httpClient;
private System.Timers.Timer aTimer = new System.Timers.Timer(2000); // 2秒轮询一次
private bool isPolling = false; // 轮询状态标志
private int pollingCount = 0; // 轮询次数计数器
private const int MaxPollingAttempts = 30; // 最大轮询次数(60秒,超时自动刷新)
public frmLogin()
{
InitializeComponent();
this.Text = "系统登录";
httpClient = new HttpClient();
}
// 窗体加载时初始化二维码和轮询
private async void frmLogin_Load(object sender, EventArgs e)
{
string imageUrl = "http://106.14.223.151:18003/api/WeChat/loginCode";
await LoadImageFromUrlAsync(imageUrl);
isPolling = true;
// 绑定轮询事件
aTimer.Elapsed += new System.Timers.ElapsedEventHandler(TimedEvent);
aTimer.AutoReset = true; // 设置持续轮询
aTimer.Enabled = true; // 启用定时器
}
// 从接口加载二维码图片
private async Task LoadImageFromUrlAsync(string imageUrl)
{
try
{
ShowLoadingIndicator(); // 显示加载状态
// 下载图片字节流
byte[] imageBytes = await httpClient.GetByteArrayAsync(imageUrl);
// 转换为图片并显示
using (MemoryStream ms = new MemoryStream(imageBytes))
{
pictureBox1.Image = Image.FromStream(ms);
pictureBox1.SizeMode = PictureBoxSizeMode.Zoom; // 自适应缩放
}
}
catch (Exception ex)
{
MessageBox.Show($"加载二维码失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
ShowErrorImage(); // 显示错误图片(可自行实现)
}
}
// 轮询事件:查询扫码状态
private async void TimedEvent(object sender, ElapsedEventArgs e)
{
if (!isPolling) return;
pollingCount++;
// 超过最大轮询次数(60秒),刷新二维码
if (pollingCount > MaxPollingAttempts)
{
StopPolling();
this.Invoke(new Action(async () =>
{
// 加载新的二维码(超时标识)
await LoadImageFromUrlAsync("http://106.14.223.151:18003/api/WeChat/loginCode?sign=timeout");
// 重置轮询状态,重新开始
pollingCount = 0;
isPolling = true;
aTimer.Enabled = true;
}));
return;
}
try
{
// 调用轮询接口
string pollingUrl = $"http://106.14.223.151:18003/api/WeChat/LoginState";
var response = await httpClient.GetAsync(pollingUrl);
if (response.IsSuccessStatusCode)
{
string jsonResponse = await response.Content.ReadAsStringAsync();
Console.WriteLine("轮询结果:" + jsonResponse);
JObject result = JObject.Parse(jsonResponse);
// 状态码200表示接口正常响应
if (result["statusCode"]?.ToString() == "200")
{
JObject value = result["value"] as JObject;
// 包含openId表示扫码成功
if (value != null && value.ContainsKey("openId"))
{
StopPolling(); // 停止轮询
this.Invoke(new Action(async () =>
{
// 加载扫码成功标识图片
await LoadImageFromUrlAsync("http://106.14.223.151:18003/api/WeChat/loginCode?sign=success");
string openId = value["openId"].ToString();
Define.OpenId = openId; // 存储OpenId到全局变量
MessageBox.Show($"登录成功!用户OpenId:{openId}", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
this.DialogResult = DialogResult.OK; // 关闭登录页,返回主页面
}));
}
}
else
{
// 接口响应异常,刷新二维码
StopPolling();
this.Invoke(new Action(async () =>
{
await LoadImageFromUrlAsync("http://106.14.223.151:18003/api/WeChat/loginCode?sign=timeout");
pollingCount = 0;
isPolling = true;
aTimer.Enabled = true;
}));
}
}
}
catch (Exception ex)
{
Console.WriteLine($"轮询异常:{ex.Message}");
// 异常不停止轮询,仅记录日志
}
}
// 停止轮询
private void StopPolling()
{
isPolling = false;
aTimer.Enabled = false;
}
// 显示加载状态(可根据项目需求实现,如显示加载动画)
private void ShowLoadingIndicator()
{
// 示例:pictureBox1.Image = Properties.Resources.Loading;
}
// 显示错误图片(可根据项目需求实现)
private void ShowErrorImage()
{
// 示例:pictureBox1.Image = Properties.Resources.Error;
}
// 释放资源(可选,优化内存)
protected override void Dispose(bool disposing)
{
if (disposing)
{
httpClient?.Dispose();
aTimer?.Dispose();
pictureBox1?.Dispose();
}
base.Dispose(disposing);
}
}
}
5. 集成步骤(4 步完成)
- 在 WinForm 项目中创建
frmLogin窗体,添加pictureBox1控件(用于显示二维码); - 通过 NuGet 安装依赖包(Newtonsoft.Json、CefSharp.WinForms、DevComponents.DotNetBar);
- 复制上述完整代码到
frmLogin.cs文件中,确保命名空间zhuang.codeprint.winform.Frms与项目一致; - 检查全局变量
Define.OpenId是否存在,若不存在需创建Define类并添加静态属性:
public static class Define
{
public static string OpenId { get; set; }
}
- 运行项目,登录窗体将自动加载二维码,扫码成功后自动存储 OpenId 并跳转主页。
四、注意事项
- 安全校验:Web 端务必保留
event.origin校验,仅允许http://106.14.223.151:18007的回调,防止恶意请求; - 依赖安装:WinForm 项目需确保所有依赖包版本匹配,避免兼容性问题;
- 控件配置:WinForm 窗体需添加
pictureBox1控件,且SizeMode设为Zoom; - 接口稳定性:当前演示接口为测试环境,生产环境建议自行部署服务(后续将开放部署教程);
- 二维码有效期:默认 60 秒超时自动刷新,可通过修改
MaxPollingAttempts参数调整(如改为 15 表示 30 秒超时); - 全局变量:
Define.OpenId用于存储用户唯一标识,后续可通过该值实现用户信息查询、权限校验等逻辑。
五、后续迭代计划
-
开放完整部署教程,支持用户自行搭建服务;
-
增加自定义二维码样式(Logo、颜色、尺寸);
-
支持获取微信用户昵称、头像等基础信息;
-
增加扫码登录日志、异常重试机制;
-
适配更多平台(Unity、Electron 等)。
如果大家有其他需求或遇到集成问题,欢迎在评论区留言,我会第一时间回复并迭代优化~