简介:OAuth2.0授权码模式是现代应用实现第三方身份认证的通用协议,其核心在于通过授权码换取令牌。在桌面开发领域,WinForm等客户端缺乏Web端浏览器重定向机制,扫码登录的回调处理成为技术难点。借助微软Edge WebView2控件内嵌Chromium内核,可捕获企业微信扫码回调中的auth_code,并调用接口换取access_token,最终获取用户身份信息。该方案兼容性好、拦截回调干净,适用于企业内部管理软件的统一身份认证改造。文章从开放平台配置、URL参数构造、回调拦截、令牌换取到本地会话维护,完整演示了WinForm+WebView2实现企业微信扫码登录的落地过程,并针对可信域名、接口选型、令牌存储等典型问题给出排查建议。
1. 项目概述:这个WinForm扫码登录到底要解决什么问题
先说结论:企业微信的扫码登录,本质上是让桌面客户端借助企业微信的OAuth2.0授权体系,完成用户身份认证并拿到用户信息,从而在自己的系统里建立一套本地会话。这个案例的核心在于,WinForm是桌面端,不像Web端有现成的跳转地址和回调页,所以如何处理回调、如何拿code、如何换token,是整套方案里的关键卡点。
我接手这个需求的时候,对方公司的业务背景是这样的:他们内部有一套基于WinForm的老管理软件,一直在用账号密码登录,但业务方多次反馈账号密码容易泄露、员工离职后账号交接混乱,希望接入企业微信扫码登录,让员工用企业微信一扫就能进系统,权限跟着企业微信的组织架构走。这个诉求非常典型,尤其是在中大型企业内部,钉钉、飞书、企业微信三选一,大部分做客户管理的公司最终都会落到企业微信上,原因是企业微信的客户联系功能和外部联系人特性是另外两家不太好替代的。
这个案例适合谁看?主要面向三类人:
- 手上维护着WinForm/桌面客户端项目,想在企业内部推行统一身份认证的开发者;
- 第一次接触企业微信开放平台,不知道扫码登录从哪一步开始下手的入门者;
- 以及想在自己的项目里复用一套“扫码-回调-换token-建会话”通用模板的人。
如果你是做Web端的,其实也能参考,但要注意Web端有现成的重定向方案,桌面端更麻烦一些,所以这篇文章的实操部分我会重点围绕WinForm的落地细节展开。
另外提一个很多人容易忽略的点:企业微信的扫码登录和企业微信内部应用授权登录是两套不同的体系。前者走的是企业微信开放平台的“企业微信登录”能力,扫码后需要企业管理员在管理后台给应用配可信域名;后者是企业在自建应用里开通“网页授权及JS-SDK”,用户在企业微信客户端里点击应用时静默授权。简单区分就是:一个扫码,一个直接点;一个发生在PC外部,一个发生在企业微信容器内部。本文只讲前者。
2. 整体方案设计:为什么选“内嵌浏览器+回调拦截”而不是“系统默认浏览器跳转”
2.1 方案选型的核心考量
WinForm做扫码登录,我在调研阶段对比了三种主流做法,下面这个对比表基本涵盖了我当时的所有考虑方向:
| 方案 | 实现方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| A. 系统默认浏览器跳转 | 调起默认浏览器打开扫码页,浏览器回调到自定义协议(如myapp://callback?code=xxx),WinForm监听协议 | 实现最快,没有内嵌控件兼容性问题 | 用户体验割裂,用户要在浏览器和桌面端之间来回切换;协议注册还要写注册表 | 临时Demo、内部测试 |
| B. 内嵌WebBrowser控件 | 用WinForm自带WebBrowser控件加载企微扫码页,监听Navigating事件拦截回调地址 | 全桌面端体验,无外部浏览器切换 | 这个控件基于IE内核,前端兼容性差,企微扫码页部分JS表现异常,尤其在新版页面 | 老旧项目、无法引入新依赖的场景 |
| C. 内嵌WebView2控件 | 用微软Edge WebView2(Chromium内核)加载扫码页,通过NavigationStarting事件拦截回调 | 体验完整,Chromium内核兼容性好,拦截回调干净利落 | 需要额外安装WebView2 Runtime,Win7不支持 | 正式项目首选 |
我最终选了方案C。原因很简单:企业微信扫码登录页面对JS的执行要求比较高,WebBrowser的IE内核在新版页面下经常出现二维码渲染不出来、轮询状态卡死的情况,这个问题在知乎上就有不少讨论,实际用起来确实如此。WebView2是微软官方推荐的控件,也是在GitHub上多数开源扫码登录项目里普遍使用的方案,长期维护有保障。
多说一句,WebView2的Runtime在Win10和Win11上通常是预装的,但Win7环境就要单独装。如果你要覆盖Win7用户,记得在部署包里面附带WebView2Runtime安装引导,或者做一个运行时检测,没装的话提示用户去下载。别等用户点开扫码页面才发现白屏,那就被动了。
2.2 企业微信扫码登录的完整数据流
在动手写代码之前,整个流程必须先在脑子里跑通。这个流程不复杂,但每一步都有它的坑:
- 用户在WinForm内嵌的WebView2中打开企业微信扫码登录页,二维码显示出来。
- 用户使用手机企业微信App扫码,并在手机上确认登录。
- 企业微信服务器让二维码所在页面产生一个回调跳转,回调地址的URL里携带临时授权码(
auth_code)。 - WinForm内嵌浏览器通过
NavigationStarting事件,捕获到这个回调URL,并从中读取auth_code。 - WinForm拿着
auth_code,在后台请求企业微信开放平台的接口,换取access_token。 - 用
access_token调用企业微信接口,拿到用户信息(如userid、姓名、头像、所属部门等)。 - 在本地系统里建立会话状态(比如写Session文件、数据库Token记录、内存态缓存),然后关闭WebView2扫码窗口,进入主界面。
这7步里,我在实际开发中栽过跟头的是第3步。原因在于,企业微信扫码登录的回调地址是可以按需配置的,但这个回调地址必须和你配置的“可信域名”完全匹配,而且这个可信域名需要企业微信管理员在管理后台审核配置。开发阶段要调通,最快的方式是自己先用一个测试环境的域名把流程跑通,再切到正式域名,因为你在本地调试时会发现回调域名配不对,二维码页会直接提示“redirect_uri参数错误”。
整个流程的授权类型本质上就是OAuth2.0里的Authorization Code模式。如果你熟悉Web开发,理解起来没什么压力。WinForm要做的事情,就是把这个本来应该由浏览器完成的“回调”动作,内聚到自己的桌面客户端里。
2.3 为什么必须自己维护一套用户态会话
企业微信扫码登录只是拿到了“这个人是企业微信里的谁”,但这不等于本地系统就认识他了。你的业务系统大概率有自己的用户表、角色表、权限表,所以拿回userid之后,还需要做一层“绑定”或“映射”。
这个映射关系常见的做法有两种:
- 一对一绑定:企业微信的userid对应业务系统的user表,登录后自动创建或不创建新用户,直接映射到已有账号。
- 首次扫码自动注册:没有绑定关系的userid,扫码后自动在业务系统里创建账号,默认分配访客角色,后续管理员再调整。
我这次项目用的是第二种,但加了一个限制条件:扫码登录只允许来自同一个企业ID(corpid)的用户注册,避免外部企业的人扫了码进不来,或者产生脏数据。这里有个很容易忽略的坑——企业微信扫码登录返回的userinfo里,corpid字段标识的是用户所属企业,一定要额外校验这个值和你们应用配置的corpid一致,不然不同企业主体下同名的userid会串号。
会话维护这块,桌面端和Web端的思路不太一样。Web端有Cookie/Session机制,浏览器天然帮你维护了会话状态。桌面端你得自己设计,我这边的做法是:本地保存一份加密的Token文件,包含access_token、userid、过期时间和用户基本信息,每次操作前读取并校验有效期,过期就重新拉起扫码窗口。这比每次打开都让用户扫码体验好太多,实测下来,只要令牌不过期,用户基本无感知。
3. 核心细节:企业微信开放平台配置与参数获取
3.1 创建企业微信应用并获取三个关键参数
在企业微信开放平台(开发者中心)配置应用时,一共有三个参数是你后续代码里绕不开的:
- 企业ID(CorpID):在企业微信管理后台的“我的企业”-“企业信息”里查看,一串以
ww开头的字符串,这是你所有接口调用的全局唯一标识; - 应用Secret:在“应用管理”-“自建应用”里创建应用后获得,这个密钥相当于应用的企业微信密码,调用接口换取access_token时要用。
- AgentId:自建应用创建后生成的内部应用标识,很多场景下要配合CorpID使用。
注意:如果你做的是第三方应用(服务商应用),参数会多一个ProviderSecret和SuiteID,流程会复杂很多。我这里讲的是企业内部自建应用的标准情况,也是大多数企业软件的落地方式。
配置过程中有一个细节容易被卡住:可信域名。企业微信扫码登录的回调地址,要求域名必须是通过ICP备案的企业域名,同时要在管理后台的“网页应用及JS-SDK”里完成域名归属验证。验证方式是下载一个校验文件放到域名根目录,确认后这个域名就成为可信域名。开发阶段如果你的服务器还没有正式域名,可以先在本地hosts里映射一个域名,但仍需要该域名完成备案。如果实在没有,可以用内网穿透工具把本机服务映射到公网,然后在该域名下放校验文件,这是一个短期内可行的过渡方案。
3.2 扫码登录的URL拼接规则与参数说明
企业微信扫码登录页面有两种引导方式:
- 方式一:构造独立登录页面链接,用户访问后显示二维码。
- 方式二:通过
https://open.work.weixin.qq.com/wwopen/sso/qrConnect?appid=CORPID&agentid=AGENTID&redirect_uri=REDIRECT_URI&state=STATE这样的地址直接拉起。
实际操作中,代码里需要拼接的URL参数主要有这几个:
| 参数名 | 是否必传 | 说明 |
|---|---|---|
| appid | 是 | 企业ID(CorpID) |
| agentid | 是 | 应用AgentId |
| redirect_uri | 是 | 回调地址,需要URL编码,必须与可信域名一致 |
| state | 是 | 自定义状态参数,建议用随机字符串防止CSRF,企业微信号会原样带回 |
| lang | 否 | 展示语言,默认中文 |
我第一版调试的时候把redirect_uri填成了http://localhost:8080/callback,结果扫码后一直报错,后来才发现企业微信后台不会把localhost当作可信域名处理。这个坑很多人都踩过。开发阶段建议用内网穿透工具先撑起来,把域名映射到本机,然后在这个域名下放校验文件,这样联调效率高很多。
3.3 换取access_token的两种场景
企业微信的access_token分为两类,容易混淆:
- 一类是应用的
access_token,通过CorpID + Secret调用/gettoken接口获取,有效期为7200秒; - 另一类是登录授权的
access_token,通过扫码后获得的auth_code换取,用于获取用户身份信息。
严格来说,扫码登录流程中我们要用的是第二类。调用的接口是:
GET https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo?access_token=ACCESS_TOKEN&code=AUTH_CODE这里有个大坑——getuserinfo接口的access_token不是直接通过CorpID+Secret获取那个,而是通过登录应用(logintype=corp)的access_token。实际操作时,要把这里的access_token换成“服务商”或“企业自建应用登录”场景下的token。我第一版就是把/gettoken接口获取的普通token直接塞进去了,结果半天返回60011错误,查文档才恍然大悟。
正确步骤是:
- 用CorpID + Secret调用
/cgi-bin/gettoken,拿到应用级access_token; - 在扫码回调里拿到
auth_code; - 再用
/cgi-bin/auth/getuserinfo?access_token=应用access_token&code=auth_code获取用户信息。
也就是说,这里的access_token确实是应用级token,但接口路径必须是auth/getuserinfo而非user/getuserinfo。前者是登录授权专用,后者是通讯录管理用的,两者不能混用。我当初就是因为接口路径选错,导致返回的用户数据为空,排查了很久。
4. 实操环节:WinForm + WebView2的完整落地步骤
4.1 环境准备与创建项目
开发工具我用的Visual Studio 2022,项目类型选择.NET Framework 4.7.2(我自己项目还在用老框架,所以这里以Framework为例),Windows窗体应用。
NuGet安装WebView2包:
Install-Package Microsoft.Web.WebView2如果你的项目是.NET 6/8,NuGet安装方式一样,但注意WebView2在.NET Core下需要额外设置一下运行时版本和用户数据文件夹,这个后面会谈到。
创建一个登录窗体,把WebView2控件拖上去,尺寸建议设置为720x540,这个比例刚好可以完整显示企业微信二维码登录页面的所有内容,不会出现滚动条。我实测的默认页面高度是520像素左右,留出一些余量比较稳妥。
4.2 设置用户数据文件夹
这个是我一开始没有注意、后来被坑了很久的地方。WebView2控件默认会使用一个临时用户数据目录,每次启动都会重新初始化,等于没有缓存。如果你在扫码登录成功后,把用户会话信息写到了Cookie或WebView2的本地存储里,下次重启程序时这些数据就丢了。
所以一定要在初始化时指定一个固定的用户数据目录:
var userDataFolder = Path.Combine(Application.StartupPath, "WebView2Data"); var env = await CoreWebView2Environment.CreateAsync(null, userDataFolder); await webView.EnsureCoreWebView2Async(env);这样扫码登录过程中的Cookie和LocalStorage才会持久化。当然,如果你的方案不依赖WebView2保存登录态,这一步可以跳过。但设置固定目录还有个好处:避免每次启动加载WebView2 Runtime的时间过长,实测能快不少。WebView2在临时环境下首次启动需要初始化用户数据,有时候会白屏一两秒钟,固定目录之后就没了。
4.3 加载扫码页并拦截回调
初始化完成后,设置导航事件:
webView.NavigationStarting += WebView_NavigationStarting; webView.Source = new Uri("https://open.work.weixin.qq.com/wwopen/sso/qrConnect?appid=CORPID&agentid=AGENTID&redirect_uri=REDIRECT_URI&state=STATE");注意,这里的redirect_uri参数里的地址一定要做UrlEncode,不然里面的&会被当成URL参数分隔符,导致企业微信后台解析错误。我见过有人直接把http://xx.com/callback?source=1这个地址原样拼接进去,结果回调过来的地址里source参数丢了。
拦截事件的核心代码:
private void WebView_NavigationStarting(object sender, CoreWebView2NavigationStartingEventArgs e) { var uri = e.Uri; if (uri.StartsWith("https://yourdomain.com/callback")) { // 从URL中解析code和state var query = new Uri(uri).Query; var parameters = System.Web.HttpUtility.ParseQueryString(query); var code = parameters["code"]; var state = parameters["state"]; // 自己的会话校验逻辑 if (!string.IsNullOrEmpty(code)) { // 异步处理换token、获取用户信息 ProcessAuthCode(code); } // 阻止继续导航到回调页面 e.Cancel = true; } }这里最关键的是e.Cancel = true。如果不取消导航,WebView2控件会直接跳到回调地址里配置的落地页,而我们在WinForm里并没有这个落地页,用户会看到一片白屏或者404页面,体验会非常糟糕。取消之后,留在原页面或者自己关闭扫码窗口,体验就自然很多。
我实际测试时遇到过一个问题:企业微信扫码登录的回调URL可能不是单次跳转,而是先跳到一个中间地址,再二次跳转到最终的redirect_uri。所以早期我在拦截事件里只判断了一次,第一次拦截到一个https://open.work.weixin.qq.com/...的中间地址,里面没有code,让我一度以为参数没传对。后来我加了日志把所有导航地址都打出来,才发现跳转链路。解决方案很简单:在事件里既判读域名是否包含yourdomain.com,也要判断URL里是否真的包含code参数,二者都满足才走登录逻辑。
4.4 换取access_token与获取用户身份
处理auth_code的后台逻辑,是整套流程里最核心的一个环节。我用HttpClient来实现:
private async void ProcessAuthCode(string code) { // 1. 获取应用access_token var tokenUrl = $"https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={corpId}&corpsecret={secret}"; var tokenJson = await httpClient.GetStringAsync(tokenUrl); var tokenObj = JObject.Parse(tokenJson); var accessToken = tokenObj["access_token"]?.ToString(); // 2. 用code换取用户身份 var userInfoUrl = $"https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo?access_token={accessToken}&code={code}"; var userJson = await httpClient.GetStringAsync(userInfoUrl); var userObj = JObject.Parse(userJson); // 3. 提取关键字段 var userId = userObj["userid"]?.ToString(); var userTicket = userObj["user_ticket"]?.ToString(); var deviceId = userObj["deviceid"]?.ToString(); }这里要重点留意:getuserinfo接口返回的数据是有限的,通常只包含userid、deviceid和user_ticket,头像、姓名、部门这些详细信息不会在这个接口里一次性返回。如果需要展示用户昵称或头像,还需要再调用一次/cgi-bin/user/get接口,用userid去通讯录里查询详细信息,接口路径是:
GET https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&userid=USERID这个接口返回的是完整通讯录信息:姓名、头像、手机号、邮箱、部门列表、直属上级等,是接入方拿用户信息的标准路径。
4.5 状态管理:从扫码完成到进入主界面
拿到用户信息后,不是立刻显示主窗体就完事了。你需要考虑以下三件事:
- 本地会话如何存储?我推荐用一个二进制序列化的凭证文件,键值对保存token、过期时间、userid和基本资料,用DPAPI加密一下。不要纯文本存储,毕竟token的效力等同于用户的登录凭证,泄露了等于账号被拿走。
- 主窗体怎么知道用户已经从扫码窗体的异步回调里返回了?用事件委托或者一个简单的静态变量都行。我喜欢用一个
LoginResult事件,扫码窗体在ProcessAuthCode完成后触发这个事件,主窗体收到后开始初始化界面和业务数据。 - 如果扫码成功但当前用户不在系统允许列表里怎么办?这里有两种策略:一种是静默注册并分配默认角色,另一种是拒绝登录并提示联系人理员。我建议至少记录一条审计日志,方便后续排查。
做完这些,核心流程已经通了。剩下的就是把这个流程和你的业务系统用户表进行映射,比如用户表增加wecom_userid字段,登录后根据这个字段找到本地用户,找不到就自动创建。
5. 常见问题与排查技巧实录
5.1 问题速查表
下面这些是我实际开发中遇到的问题,按出现频率排序:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 扫码后提示“redirect_uri参数错误” | 回调地址与可信域名不一致;回调地址未URL编码 | 检查可信域名配置;用HttpUtility.UrlEncode重新编码 |
| 二维码加载不出来,白屏 | WebView2 Runtime未安装;网页被本地安全策略拦截 | 检查运行时;设置为https混用模式;查看CoreWebView2Environment异常 |
| 扫码后手机确认了,但页面一直转圈 | 轮询状态未正确触发;企业微信服务器回调失败 | 在NavigationStarting里输出所有URL,确认有无跳转 |
| 拿到code但调用接口报40014 | code过期或已被使用 | 确认code有效期(5分钟),生成后立即使用;不要重复调用 |
| 调用接口返回60011 | 权限里没有配置“登录授权”的Secret | 检查是否用错secret;确认应用类型是“自建应用” |
| 返回用户信息为空 | 调用的接口路径错误(用成了通讯录接口) | 确保使用/cgi-bin/auth/getuserinfo |
| Win7上WebView2控件不初始化 | Win7不支持新版本WebView2 Runtime | 需要特殊处理或降级到WebBrowser方案 |
5.2 企业微信回调地址拿不到code的排查
这个问题的排查思路值得单独拎出来讲,因为它的成因比较隐蔽,而且一旦发生往往让新手摸不着头脑。
核心症状:扫码后在手机上确认了,内嵌浏览器里URL变了,但代码里拿到的query参数里没有code,或者URL里根本没有出现你配置的redirect_uri。
排查步骤:
第一,在企业微信后台的“应用管理”里找到你的自建应用,点开“网页应用及JS-SDK”,确认“可信域名”配置了你当前使用的域名。这个域名要求必须经过ICP备案,校验文件也必须在域名根目录下可以访问到。如果你用了子域名,校验文件要放在子域名根目录,不能只在主域放一份。
第二,确认你打开的扫码链接里,redirect_uri参数的编码是否正确。这个参数需要先编码再拼接到完整URL里,不能被后面的&state=参数截断。如果编码不正确,企业微信后台会解析出和预期不一致的回调地址,直接导致授权失败。
第三,观察NavigationStarting事件里所有的跳转地址。我写了一个简单的日志:把每次导航的URL输出到Debug窗口,扫码一次,就能看到完整的跳转过程。通常顺序是:企微域名下的一个中间页 -> 回调域名下的落地页。如果你只拦到了中间页,千万别关掉拦截逻辑,继续追踪下一个URL。
5.3 多环境切换的配置管理
开发、测试、正式三个环境,企业微信可信域名各不相同,代码里如果把URL硬编码死了,切换环境就是一个灾难。我建议把下面几个配置项单独从app.config里读:
WeComCorpId:企业ID,每个环境可能不同。WeComAgentId:应用ID。WeComSecret:应用密钥,注意不要提交到版本库。WeComCallbackUrl:回调地址。WeComApiBaseUrl:API地址,默认https://qyapi.weixin.qq.com。
环境切换时只需要改配置文件。另外正式环境的Secret别写在代码里,最好用环境变量或配置密钥服务保管,避免源码泄露后被人直接调API。
5.4 用户扫码成功但登录失败的后台追踪
扫码成功但系统里没有用户,或者权限不对,这类问题属于业务层映射问题。我们的做法是在扫码成功的回调里加一条审计日志,记录userid、auth_code、当前时间、设备信息。这样即使出问题,也能快速定位是用户不存在、角色没配还是系统时间偏差等问题。
有一回用户反馈扫码后系统提示“用户不存在”,我看日志发现userid是正常的,但本地用户表和企微userid的对应关系没有初始化。原因是他们在企微里新建了一个用户,但同步任务还没跑。后来我在扫码登录逻辑里加了一个判断:如果userid在映射表里找不到,自动调用user/get去企微通讯录里拉一次完整信息,并按规则创建用户。这大大减轻了运维负担,但需要额外注意的是,自动创建的用户隐私保护要做好,默认角色必须是只读的,管理员再自行提升权限。
6. 踩坑总结与优化建议
6.1 一定要在生产环境走一遍完整流程
开发环境下所有接口都通,不代表正式上线就没问题。最主要的原因是生产环境的网络策略、域名解析、防火墙设置都可能和开发环境不一样。我自己遇到过的一个情况是:开发机上WebView2可以直接访问企业微信的回调服务,但生产环境的某台服务器禁用了TLS某些协议,导致WebView2页面加载一半失败。这个排查起来很头疼,因为你无法直观看到WebView2内部产生的网络错误。建议在初始化时订阅CoreWebView2.WebResourceFailed等事件,把失败的资源请求记录下来,方便线上定位。
6.2 适当使用轮询而不是完全依赖回调
企业微信扫码登录官方推荐的是回调模式,但实际开发中回调跳转会受内嵌浏览器策略影响。我后来加了一个兜底机制:在用户扫码后,WinForm端定时轮询一个本地接口,检查当前auth_code是否已经被处理。这个轮询不是对接企业微信服务器,而是检测本地后台的异步任务状态。这样即使NavigationStarting干扰导致回调被跳过,后台仍然能拿到code完成登录。最终效果是页面二维码点了确认,最多5秒内就会进入主界面,几乎感觉不到延迟。
6.3 从“能登录”到“好用”的细节打磨
登录流程跑通后,我陆续加了一些体验优化:
- 二维码显示期间,窗口右上角加一个“刷新”图标,防止用户扫了半天二维码过期了还得关窗重来;
- 在登录窗口上显示当前扫码的“企业名称”,让用户确认自己扫的是不是对的企业的码;
- 登录完成后,把企业微信返回的头像和姓名以及本地系统的角色一起展示在主窗体状态栏,用户一眼就能看出当前身份;
- 如果本地已经存在有效的会话Token,直接将登录窗体隐藏,直接展示主界面,减少用户等待。
这些细节不做也不会出错,但做了之后内部同事反馈“这个登录比起之前账号密码顺滑多了”,这就是正向反馈。
6.4 安全层面的零信任思维
扫码登录虽然省去了输密码的步骤,但安全风险一点都不少。有几个点特别提醒一下:
- 生产环境Secret不能暴露,一旦泄露,别人可以拿着你的Secret调企业微信全部接口,包括通讯录,后果很严重;
state参数必须校验。企业微信原样返回state,如果你的服务端不做校验,攻击者可以伪造回调请求,实现CSRF登录。我的做法是生成state时带上当前时间戳和随机数,服务端校验有效期和随机数。- 本地Token存储要加密。用DPAPI加密文件最稳妥,不要为了省事把Token明文存在ini或json里。
- access_token是敏感凭证,不要打进日志里。打日志时只输出userid和部分信息,完整token一律脱敏。
做完这一套,整个扫码登录功能从需求评审到上线,前后大概花了一周时间。真正写代码的时间其实不多,大部分时间都花在理解企业微信授权机制、调试回调跳转、以及处理各类环境的兼容问题上。核心经验总结起来就一句话:先弄清数据流,再动手写代码,遇到问题先看日志,不要凭感觉猜。
最后分享一个很实用的小技巧:在企业微信开发者后台调试时,利用扫码页本身自带的调试模式,在URL后加上&debug=1,页面上会显示当前的授权进度和请求信息,这个对排查回调失败问题非常有帮助。很多人不知道这个参数,但它在定位问题时省了我不少时间。
本文还有配套的精品资源,点击获取