资讯详情

微信扫码登录网站应用:OAuth2.0流程与PHP实现详解

📅 2026/10/9 5:53:15 | 华诺云谱 👁 阅读
微信扫码登录网站应用:OAuth2.0流程与PHP实现详解
做网站登录注册的时候微信扫码登录绝对是个绕不开的需求。用户不愿意记密码又不想收短信验证码掏出手机扫一下就能进这对转化率的提升非常明显。但真正动手做的时候你会发现微信官方的文档写得那叫一个绕一会儿开放平台一会儿公众平台AppID和AppSecret一堆redirect_uri还老是报错新手很容易栽跟头。这篇我就把微信扫码注册登录-基于网站应用这个需求完整地拆一遍从方案选型到代码落地再到上线排错一次讲透。先说一下本文的适用对象你的网站是传统的PC端网页用浏览器访问想增加一个微信扫码注册/登录入口。注意这句话的重点——PC端网页。如果你的场景是微信小程序、公众号内嵌H5或者App内拉起微信登录那技术路径完全不同别混着看。我会在后文把这几种场景的边界讲清楚帮你避免选错方案白忙活一场。1. 先分清两套微信登录体系开放平台扫码与公众号授权1.1 为什么扫码登录有两种方案而不是一种很多第一次接触微信登录的开发者最大的困惑就是我去百度搜微信扫码登录出来的结果乱七八糟有人让你去微信开放平台有人让你去微信公众平台还有人让你用扫一扫的接口。其实归根到底微信官方现在能支撑网站登录的体系就两套微信开放平台的网站应用扫码登录就是用户用微信扫一扫去扫你网页上的二维码扫码后手机微信上弹出确认页面点同意网页端就登录成功。这是官方专门为PC网站设计的登录方式最正统。微信公众号内的网页授权这是给公众号里打开的H5页面用的用户在微信里点开你的链接直接授权获取身份。如果强行用到PC端你得让用户先在微信里打开某个页面或者通过公众号二维码扫码关注来换取身份属于曲线救国。这两套体系的应用场景完全不同AppID也不通用。开放平台的AppID是给网站应用移动应用小程序用的公众平台的AppID是给公众号用的。你把开放平台的AppID拿去调公众号接口或者反过来都会直接报错。这个坑我见过太多人踩了所以放在第一个说。1.2 开放平台网站应用的能力边界开放平台的网站应用扫码登录调用的是OAuth2.0授权流程它给你什么、不给你什么要提前清楚能拿到用户的UnionID、OpenID、昵称、头像。注意男女、地区这些信息在2021年之后就收紧了很多新注册的应用基本只能拿到头像和昵称性别和地区经常是空值。拿不到手机号。这是很多做注册系统的开发者没搞清楚的地方网站应用扫码登录永远不会返回手机号。想拿手机号只有微信小程序里的getPhoneNumber组件能做到还得是认证过的企业小程序、用户主动授权才行后面我会专门展开讲。用户扫码后手机上会有一个确认登录的页面这个体验是微信官方控制的你改不了。用户如果没关注过你的公众号也不影响扫码登录因为这两种身份体系是分开的。所以如果你只是想让用户扫一下就能登录顺便拿到头像昵称那开放平台扫码登录是绝对的正道。别去折腾什么先关注公众号再登录的骚操作除非你另外有公众号涨粉的需求。1.3 什么时候你只能用公众号体系这是很多人搞混的另一头。如果你的网站首页是在微信内置浏览器里打开的比如用户通过公众号菜单、朋友圈链接、聊天里的转发进入你的H5页面这时候你想让他免密登录就应该走公众号网页授权snsapi_userinfo而不是开放平台的扫码登录。原因很简单在微信内置浏览器中页面有一个环境标识微信的JSSDK和网页授权能直接识别出用户身份不需要用户再掏出手机扫一次。你让他再扫一次码体验极其愚蠢。判断方法也很简单在后端看一下请求头里的User-Agent里面包含MicroMessenger就说明是微信内置浏览器。很多网页的做法是检测到微信浏览器就走公众号授权检测到是PC浏览器就调起扫码登录。这个双通道的方案其实才是很多商业网站的真实落地形态。2. 开放平台网站应用扫码登录从注册到OAuth2.0落地2.1 开放平台注册与开发者资质认证要使用网站应用扫码登录第一步是在 微信开放平台 注册账号并进行开发者资质认证。这里要提前打个预防针开放平台的开发者资质认证是要收费的从我记得的行情来看是300元人民币每次审核而且这个认证不是永久的需要年审。你要是只想做个个人博客或者学习Demo这笔钱可能会让你犹豫。不过现实就是微信把扫码登录接口的权限和认证绑定了不认证你就创建不了网站应用所以商业项目基本没得选。创建网站应用时你需要填一堆信息包括网站名称、网站域名、网站简介、备案号等。审核周期一般是1-7个工作日快的当天就能过。我在实际项目里遇到最费时间的不是审核而是域名备案信息对不上这里建议提前准备好信息项注意事项网站名称和备案名称保持一致不要花里胡哨网站域名填写主域名如example.com不要带协议头回调域名填写www.example.com也不能带https://备案号必须真实有效否则审核直接驳回2.2 网站应用的创建与回调域名配置应用创建成功后你会拿到两个核心凭证AppID和AppSecret。AppSecret非常敏感它相当于这个应用的密码任何人拿到它都能冒充你的应用去调微信接口。所以后端的代码里一定不要硬编码AppSecret更不要把它下发到前端JavaScript里这个我后面排错部分还会强调。接下来是设置授权回调域路径是开放平台后台 → 网站应用 → 查看 → 开发信息 → 授权回调域。这里有个细节很多人会搞错授权回调域只需要填域名不要填完整的URL路径而且不能带协议头。比如你的完整回调地址是https://www.example.com/wx/login/callback那回调域就填www.example.com就够了。如果填成带协议头或者带路径的扫码后微信会一直报redirect_uri参数错误。2.3 授权流程拆解与核心代码实现PHP版整个开放平台扫码登录的时序是这样的前端页面展示一个二维码用户用微信扫一扫扫描。微信服务器生成确认页面用户在手机上点击同意登录。微信服务器302重定向到你的回调地址带上授权临时票据code。你的后端用code换access_token和openid。后端用access_token拉取用户资料昵称、头像。后端建立自己的登录态Session / JWT返回给前端。你可能已经发现了这里省略了生成二维码这一步。其实官方的方式非常取巧二维码的内容不是某个神秘的接口返回的而是一个授权URL转成的二维码图片。你只要在页面上放一个img srcqr.png让后端把这个授权URL交给前端生成二维码即可。后端用PHP实现的核心代码如下?php // 第一步生成授权URL交给前端二维码组件渲染 $appid wx1234567890abcdef; $redirect_uri urlencode(https://www.example.com/wx/login/callback); $state md5(uniqid(wx_login_, true)); // 防CSRF建议存session $authorize_url https://open.weixin.qq.com/connect/qrconnect? . http_build_query([ appid $appid, redirect_uri $redirect_uri, response_type code, scope snsapi_login, state $state, ]) . #wechat_redirect; // 把$authorize_url输出给前端前端用jquery-qrcode或zxing之类插件渲染成二维码 ?这里有三个参数要特别解释scope必须固定为snsapi_login这是开放平台扫码登录专用的改成snsapi_userinfo会失败。state是防跨站请求伪造的自定义参数微信回调时会原样带上。你必须在回调里校验这个值和发起登录时保存的值一致否则可能是被恶意利用了。URL末尾那个#wechat_redirect是微信HTML5页面跳转的老规矩不要去掉。2.4 用户信息拉取与Session建立用户扫码并确认后微信会重定向到你的回调地址形如https://www.example.com/wx/login/callback?codexxxstateyyy你需要在回调接口中做两件事校验state然后用code换token。注意这个code只能用一次而且有效期很短一般是5分钟。换token的接口是GET https://api.weixin.qq.com/sns/oauth2/access_token?appidAPPIDsecretSECRETcodeCODEgrant_typeauthorization_code返回的JSON长这样{ access_token: 40_fG1eJbF5xxx, expires_in: 7200, refresh_token: 40_xxxx, openid: oYx9K0abc123, scope: snsapi_login, unionid: oD3Kxxx }然后拿access_token去拉取用户资料接口为GET https://api.weixin.qq.com/sns/userinfo?access_tokenACCESS_TOKENopenidOPENID这一步有个非常容易忽略的坑上面的access_token和网页授权、小程序登录的access_token是不同体系的不能混用。开放平台sns/oauth2/access_token返回的token只能用来拉取用户信息不能用来调cgi-bin下的接口更不能拿去当公众号全局token。你在网上搜索时会看到很多资料互相抄抄来抄去把两个接口的域名搞混了调试半天才发现。拿到用户资料后我的建议做法是以openid或unionid作为唯一标识去用户表查记录。查不到就创建一条新用户记录状态标记为微信新用户昵称头像用微信返回的。创建成功后再在服务端下发你自己的登录态比如生成一个JWT或者PHP里$_SESSION[user_id]看你的技术栈。前端拿到登录态之后整个注册登录流程就闭环了。整个过程写完整大概长这样?php // 第二步回调处理 session_start(); $state $_GET[state] ?? ; if ($state ! $_SESSION[wx_login_state]) { die(state校验失败); } $code $_GET[code] ?? ; $appid wx1234567890abcdef; $secret your_app_secret; $token_resp file_get_contents(https://api.weixin.qq.com/sns/oauth2/access_token?appid{$appid}secret{$secret}code{$code}grant_typeauthorization_code); $token_data json_decode($token_resp, true); if (isset($token_data[errcode])) { // 记录日志返回错误提示 die(获取access_token失败 . $token_data[errmsg]); } $openid $token_data[openid]; $userinfo_resp file_get_contents(https://api.weixin.qq.com/sns/userinfo?access_token{$token_data[access_token]}openid{$openid}); $userinfo json_decode($userinfo_resp, true); // 这里省略查库、建号、发登录态的代码根据你的框架来 $_SESSION[wx_openid] $openid; $_SESSION[user_nickname] $userinfo[nickname] ?? ; $_SESSION[user_avatar] $userinfo[headimgurl] ?? ; header(Location: /user/profile);3. 没有开放平台账号时的过渡方案公众号授权二维码登录3.1 网页授权与扫码登录的差异上面那套方案需要开放平台认证但如果你是个体开发者暂时不想花这300块认证费又确实想在PC网页上让用户扫码登录其实还有一条路可以走利用微信公众号的扫一扫能力。你没看错公众号体系虽然是为微信内打开的网页设计的但它有一个生成带参数二维码的接口——就是那种扫了之后自动关注公众号的二维码或者是活动推广用的渠道二维码。这个接口允许你自定义场景值scene当用户扫码并关注公众号时微信服务器会往公众号后台推送一个消息里面带着你自定义的scene值。你可以利用这个消息推送到自己的服务器再配合公众号网页授权拼出一个扫码关注即登录的方案。但这套方案和开放平台扫码登录在体验上有本质区别对比项开放平台扫码登录公众号扫码登录过渡方案用户操作扫码 → 点击同意登录扫码 → 关注公众号或已在关注则直接提示页面确认有独立确认页面没有确认页面直接通过消息推送触发获取用户身份openid unionid 头像昵称openid 头像昵称需进一步授权用户感知我在登录这个网站我关注了一个公众号适合场景正规商业网站登录个人项目、公众号涨粉需求为主老实说这套方案的用户体验是打了折扣的因为很多用户不想为了登录一个网站而去关注公众号。但如果你本身就有公众号运营需求那这个方案等于一鱼两吃用户关注了公众号网站登录态也有了。我见过不少流量主网站是这么干的。3.2 服务器端生成带参二维码的核心逻辑公众号官方生成带参二维码的接口叫创建二维码ticket调用方式是POST https://api.weixin.qq.com/cgi-bin/qrcode/create?access_tokenACCESS_TOKEN请求体核心内容是{ expire_seconds: 604800, action_name: QR_SCENE, action_info: { scene: { scene_id: 123456 } } }这个接口返回一个ticket然后你可以用这个ticket拼接出二维码图片地址https://mp.weixin.qq.com/cgi-bin/showqrcode?ticketTICKET实操里的细节是scene_id必须是一个数字如果你要传字符串得用scene_str而不是scene_id。这里千万别搞错微信文档写得比较隐晦很多人在这一步绕了半天发现字符串场景值一直建不出来把scene_str换成scene_id强转数字之后就好了。3.3 用消息推送完成扫码状态轮询用户扫码关注公众号后微信服务器会向公众号后台配置的服务器URL推送一个XML消息里面包含Event事件类型和EventKey你的场景值。你在后端写一个消息处理器接收这个推送解析出EventKey然后把这个场景值对应的用户已扫码这个状态写入数据库同时把用户的openid一起存下来。前端页面这边用轮询的方式每隔2-3秒调用你自己的后端接口查这个场景值对应的状态是否已经变成已扫码。如果变了后端接口就能把用户信息放出来前端拿到登录态完成跳转。这里有一个关键点用户扫码但没关注公众号时微信会先跳到一个关注确认页面用户点了关注之后才会触发推送事件。如果用户本来就关注了你的公众号扫码后会直接进入公众号对话页同时微信会推送一条SCAN事件消息里也会带EventKey。两种情况下你都能收到推送但要注意区分subscribe新关注和SCAN已关注两个不同的Event类型处理逻辑稍有不同。这套方案的PHP实现核心是消息回调?php // 微信公众号服务器配置的 回调入口这里简化处理 $xml file_get_contents(php://input); $data simplexml_load_string($xml, SimpleXMLElement, LIBXML_NOCDATA); if ($data-MsgType event) { $event strtolower($data-Event); $eventKey (string)$data-EventKey; // 临时二维码会带scene_xxxx前缀 // 去掉前缀得到纯场景ID $sceneId str_replace(scene_, , $eventKey); $openid (string)$data-FromUserName; if ($event subscribe || $event scan) { // 更新数据库该sceneId状态为已扫码记录openid update_scan_status($sceneId, $openid); } } // 必须输出 success否则微信会重试推送 echo success;3.4 过渡方案的局限清单话说回来这个过渡方案有几个硬伤你最好提前知道用户取关后登录态没法维持。不要依赖关注关系来做永久登录识别用户取关之后再做登录判断就会出问题除非你用网页授权再拿一次openid。低质用户感知。明明是个网站登录结果变成关注公众号用户心理上会觉得被套路了流失率极高。如果你公司有专门的公众号运营团队人家可能无所谓但纯做工具类网站我不推荐。无法获取UnionID。公众号得到的openid和开放平台的unionid不打通如果以后你再接入小程序或开放平台用户身份很难关联。当然现在开放平台可以和公众号绑定绑定后就能拿到UnionID不过绑定流程又是一通操作。总之过渡方案适合没有认证预算有公众号涨粉需求的极端情况。但凡你想认真做网站登录我的建议还是老老实实走开放平台。4. 注册与账号绑定把微信身份转为网站用户4.1 首次扫码等于自动注册OpenID与UnionID很多人认为扫码登录和扫码注册是两件事但在微信体系里第一次扫码就已经完成了注册——微信把用户身份交给你你只需要在本地数据库里建一条对应的记录即可。所谓自动注册是产品上的一个决策让用户扫一次就进系统扫第二次就弹头像昵称。这里核心要理解两个标识的区别OpenID同一个用户在同一个应用同一AppID下的唯一标识。也就是说用户在你网站应用的OpenID和他在你小程序里的OpenID是两个不同的字符串。不能拿小程序拿到的openid来网站应用里匹配用户。UnionID同一个微信用户在同一个开放平台账号下的所有应用网站、小程序、移动App里共享的唯一标识。只要你的多个应用都绑在同一个开放平台账号下用户在这些应用里的UnionID是同一个。这是个很关键的设计决策如果将来你的产品里同时存在PC网站、小程序、App那用户身份一定要以unionid为主键否则同一个用户PC端登录过了小程序里还要再注册一次数据就割裂了。那么UnionID什么时候能拿到网站应用扫码登录的接口返回里正常情况下会带一个unionid字段。但有个前提你的开放平台账号要和这个小程序/公众号绑定。如果没做绑定接口返回里就没有unionid只有openid。这个很容易被忽略因为文档上写着会返回实际测试却发现没有一查是因为绑定关系没建立。4.2 老用户绑定与手机号兜底自动注册虽然方便但纯微信登录的用户留给你的是一个微信身份用户信息只有昵称头像。如果哪天微信接口调整、或者用户想换绑账号你就丢失了所有触点。所以成熟的注册登录系统一般都会加一个手机号绑定步骤常见做法是首次扫码后先让用户进入一个完善资料页。页面显示微信昵称头像同时预留手机号输入框用户填完手机号验证码才算完成注册。老用户如果已经用手机号注册过点击绑定微信可以把openid和现有账号关联起来。这一步能在产品层面解决两个问题用户身份的唯一性和找回能力。反正我自己的项目从第一天就强制绑手机号后来遇到要发短信通知、做账号找回的时候就非常从容。很多只做纯微信登录的网站面对用户忘记用哪个微信扫的这种问题只能干瞪眼。验手机号的方案选哪家看你自己预算国内用阿里云短信、腾讯云短信都行注意短信模板审核时不要写注册这种词容易触发审核卡点写成验证码或安全校验通过率更高这也是实操里摸索出来的经验。4.3 小程序手机号获取与网站应用的区别前面说网站应用拿不到手机号那如果产品里原本就有小程序端很多人会想能不能在小程序里拿到手机号然后把这个手机号和网站应用账号打通答案是可以但流程不是网站应用直接返回手机号而是在小程序端用户点击微信一键登录按钮调wx.login()拿到临时code。后端拿code换openid和session_key。在小程序里加一个button open-typegetPhoneNumber用户点击并授权后前端拿到一个加密数据后端用session_key解密出手机号。把这个手机号和网站账号体系关联。这个过程有个硬性门槛只有企业主体的小程序才能调用getPhoneNumber个人主体小程序没有这个权限。另外getPhoneNumber自2023年起就改为动态验证新申请的小程序需要先在管理后台申请开通并且用户每次授权都要求用户主动点击无法静默获取。所以如果你以为能悄悄摸到用户手机号趁早放弃这个想法。顺带说一句现在很多开发者喜欢用第三方平台的一键登录组件本质还是封装了小程序的getPhoneNumber价格看调用量小站长自己开发不难没必要被中间商赚差价。5. 上线后最常踩的坑与排查思路5.1 扫码后提示redirect_uri参数错误这个是开放平台扫码登录出现频率最高的问题没有之一。原因基本就这几个授权回调域填错了带了协议头或路径正确填法是www.example.com。授权URL里redirect_uri没有做urlencode。注意像PHP里用http_build_query会自动编码但如果你手拼URL很容易漏掉。你的回调地址确实不可访问微信服务器访问不到也会报这个错。先用浏览器直接访问回调URL确认能打开。备案过期或域名变更后开放平台里的信息没更新。排查思路就是先看域名配置再看redirect_uri编码最后确认回调URL可访问。这三步走完90%的问题都能解决。我在项目里遇到最多的是第一种尤其是团队里新来的同事喜欢把整个https://www.example.com/wx/callback填进回调域里看起来好像更完整其实是错的。5.2 伪造UA能拿到OpenID吗热词里有一句php伪造微信浏览器头信息这里必须给大家泼一盆冷水伪造User-Agent不能让你拿到微信用户身份。微信公众号网页授权是否生效由微信服务器的机制决定而不是仅仅看你请求头里的UA。你在PC浏览器上把UA改成MicroMessenger确实可以触发网页的微信浏览器环境判断让前端代码跑进公众号授权的分支但真到了调微信接口那一步微信会识别出这不是一个真实的微信客户端环境授权直接失败或拿不到用户信息。这个快捷键现象我见过太多人用错了。伪造UA只是为了调试前端样式比如看看页面在微信浏览器里排版会不会乱这没问题。但想用伪造UA骗过服务端去走授权拿openid完全行不通。真实的微信授权必须依赖微信客户端内置的浏览器环境、TLS握手和微信的私有协议不是一个User-Agent字符串能模拟的。现在还有不少网上流传的微信浏览器头信息列表拿来做前端兼容测试还行别拿去搞歪门邪道。5.3 回调里没有code导致失败回调地址被访问了但$_GET[code]一直是空这种情况我也见过很多。原因大概有两种第一种是用户扫码后在手机端点了取消授权微信会带上code吗不会。此时回调URL里没有code微信会带上state和error信息比如erroraccess_denied。你的回调逻辑必须处理这种用户拒绝的情况不能直接抛异常应该回到登录页提示您取消了登录。第二种是前端把二维码链接改了。有的开发者图省事把后端的授权URL直接放到了某个第三方二维码API里生成图片结果第三方API对URL做了跳转简化扫码时用户看到的确认页域名和回调域不一致微信就会判定非法请求连回调都不发。这个坑很难排查建议二维码生成自己控制在后端或者用纯前端插件本地生成不要走第三方跳转。5.4 测试与生产环境的域名隔离最后提醒一点微信开放平台后台的授权回调域是区分环境的而且同一个域名下的回调URL不能随意改改一次似乎问题不大但频繁更换域名会导致你之前应用审核时的域名备案信息对不上甚至触发风控。所以我的做法是测试环境单独申请一个测试应用AppID用测试号的。生产环境用正式AppID回调域名固定。后端配置里把redirect_uri做成可配置项根据环境变量切换。如果公司有多个子域名比如admin.example.com和www.example.com开放平台允许你配置多个授权回调域可以在后台添加不用每个子域名都单独建一个应用。另外再说一个安全上的点AppSecret泄漏问题。我在代码审查里见过不少把AppSecret写在Git仓库里的案例只要仓库一公开立刻就会有人拿你的AppSecret去刷接口、刷粉丝甚至盗刷流量。正确做法是放环境变量或密钥管理服务并且定期在开放平台后台重置AppSecret。这个不是危言耸听接微信支付和扫码登录的坑我踩多了基本上每次安全事故都是些低级错误引起的。最后再分享一个小技巧做扫码登录联调的时候如果你是在本地开发环境调试回调微信服务器是无法访问你localhost的。这时候可以用内网穿透工具把本地端口暴露到一个临时公网域名再把临时域名配到开放平台测试应用里。联调完毕记得把回调域换回正式域名不然正式环境的二维码会一直跳转到你临时域名去。这个流程我每次做新项目时都会走一遍已经条件反射了。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑