🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## 一、概述 `OpenId`是一个用户对于一个小程序/公众号的标识,开发者可以通过这个标识识别出用户,比如微信支付的很多产品,都是通过openId来确定支付者的身份; `UnionId`是一个用户对于同主体微信小程序/公众号/APP的标识,开发者需要在微信开放平台下绑定相同账号的主体。开发者可通过`UnionId`,实现多个小程序、公众号、甚至APP 之间的数据互通了; ## 二、技术方案 ### **技术原理** (1)用户在微信客户端中访问我们开发的网页,公众号就可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑; 前提:在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的“开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息”的配置选项中,修改授权回调域名。请注意,这里填写的是域名(是一个字符串),而不是URL,因此请勿加 http:// 等协议头; ![](https://img.kancloud.cn/2d/ec/2decbb8d4aee97bddf35d1b07bb7804e_1911x851.png) (2)网页授权的两种scope 1、以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面) 2、以snsapi_userinfo为scope发起的网页授权,是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息; (3)关于特殊场景下的静默授权 1、上面已经提到,对于以snsapi_base为scope的网页授权,就静默授权的,用户无感知; 2、对于已关注公众号的用户,如果用户从公众号的会话或者自定义菜单进入本公众号的网页授权页,即使是scope为snsapi_userinfo,也是静默授权,用户无感知; ### **实现步骤** 第一步:包裹授权地址,用户同意授权,获取code 在确保微信公众账号拥有授权作用域(scope参数)的权限的前提下(服务号获得高级接口后,默认拥有scope参数中的snsapi_base和snsapi_userinfo),引导关注者打开如下页面(把实际开发的前端地址包裹到REDIRECT_URI,使用 urlEncode 对链接进行处理): ``` https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect ``` 如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE; 第二步:通过code换取网页授权access_token和openId 首先请注意,这里通过code换取的是一个特殊的网页授权access_token,与基础支持中的access_token(该access_token用于调用其他接口)不同。公众号可通过下述接口来获取网页授权access_token。如果网页授权的作用域为snsapi_base,则本步骤中获取到网页授权access_token的同时,也获取到了openid,snsapi_base式的网页授权流程即到此为止; 获取code后,请求以下链接获取access_token(也就是微信提供的获取token的api,需要参数appId和appSecret,再加上code即可): ``` https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code ``` 到这里,就已经拿到了openId; ## 三、从任意页面获取 核心思路就是: 第一步、调用获取OpenIdURL的后端api接口(普通版本或免登录任意跳转版本),并传入(固定或实时任意传入的)前端重定向地址,接口返回curAuthorizedGetOpenIdUrl中得到一个URL,该地址就是一个Oauth2包裹授权的地址; 例如:curAuthorizedGetOpenIdUrl地址示范: ``` https://open.weixin.qq.com/connect/oauth2/authorize?appid=yourappId&redirect_uri=http%3A%2F%2Fserver.yourdns.com%2Ffcios%2Fapi%2Fsystem%2Fopen%2Fweixin%2Fofficialaccount%2Foauth%2Fredirect%2Feverywhere%3FredirectUrl%3Dhttp%3A%2F%2Fmobile.yourdns.com%2Freg%26extraParameters%3D&response_type=code&scope=snsapi_userinfo&state=yourappId&connect_redirect=1#wechat_redirect ``` 第二步、前端引导用户打开curAuthorizedGetOpenIdUrl的地址,将会引导用户做授权(可以是静默授权,用户无感知),用户授权完成后,会重定向到传入的前端地址; 例如:上面的地址,微信浏览器跳转后,重定向到的地址: ``` http://mobile.yourdns.com/#/reg?openId=ZXDYD507an9mgdva7Kvny9RNxtLE&extraParameters= ``` 第三步、在重定向跳转到的前端地址页面上,前端要侦听参数,得到OpenId; >[danger] 平台提供了两个版本的获取OpenId接口,一个是固定前端重定向地址的(普通版本),一个是可以由调用时传入前端重定向地址的(免登录任意跳转版本); ### **(1)普通版本获取openId的授权访问地址接口** 为了获取当前项目的授权地址,提供了一个便利的接口: ``` /api/modules/wechat/officialaccount/getopenid/url ``` ![](https://img.kancloud.cn/fb/a9/fba95b748de1d8b04d87a7715a28c271_1920x942.png) 为了生成的授权地址是正确的,必须确保服务器部署的地址配置正确,该配置项是外部访问当前后端服务的根地址,供微信重定向给前端的时候回调到当前后端服务器; ``` rayframework.open.weixin-official-account-server-url=http://mydns/raysale ``` 微信授权后,将跳转到下面配置项定义的前端地址,在该前端地址,即可获取当前微信的openId了; ``` rayframework.open.weixin-official-account-front-url= ``` ### **(2)免登录任意跳转版本获取openId的授权访问地址接口** 无需登陆即可调用的版本,与上面版本不同的是,这里需要传入一个参数,即授权后的跳转地址redirectUrl(而非参数配置固定): ``` /api/modules/wechat/officialaccount/getopenid/url/everywhere ``` >[danger] > 1、调用的时候,传入重定向地址redirectUrl(该地址不可以有#,否则会导致无法跳转过去),即可跳转到任意指定的重定向地址,如果没有指定,则使用上面版本一样的系统配置地址,比较灵活; > 2、如果希望携带参数跳转,则需要通过参数名extraParameters来传递; > 3、接口调用之后,获取curAuthorizedGetOpenIdUrl属性的授权地址,前端跳转到该地址,该地址就会引导微信页进入授权,得到用户授权后,拿到openId,跳转到用户指定的redirectUrl地址,在用户指定地址里面,前端即可侦听并获得openId了; ### **(3)授权后重定向地址中需要#的处理办法** 如果前端必须采用hash路由模式,那怎么处理授权后重定向地址的问题呢? 解决方案是由nginx来处理; 举例来说; 1、如果前端地址需要配置为`http://mydns/home#/register`,那么实际作为redirectUrl参数传入的时候,我们考虑到微信授权重定向不支持#,那么我们把重定向地址设置为`http://mydns/home/register`传入(没有#); 2、在nginx中,配置rewrite逻辑,将`http://mydns/home/register`rewrite为`http://mydns/home#/register`,达到目的; ``` location / { rewrite ^/home/register(.*) /home#/register permanent; } ``` 完整实例: ``` location / { root html/mobile; index index.html index.htm; try_files $uri $uri/ /index.html; index index.html index.htm; rewrite ^/login(.*) /#/login permanent; } ``` ![](https://img.kancloud.cn/6c/27/6c27a6806554b80cfdbffb45053073a7_732x165.png) ## 四、从自定义菜单跳转获取 ### **(1)实现步骤** 首先、假定一个入口地址,在公众号的底部菜单配置的时候,把当前入口地址包裹oauth2授权之后的地址作为菜单URL发布; 然后、从微信中访问公众号,点击该菜单,就会在刚才定义的入口地址后面,追加上微信用户的code了; 最后、前端获取到的code,然后再结合appId和appSecret,即可取得openId;可以跟后端接口进行业务整合了; >[danger] 比如:可以跟登陆的账号信息一起提交过来,后台可以立即将系统账号与微信用户的openId进行绑定,后续业务将自动从后端关联获取openId;也可以不绑定,仅仅让前端持有,那么,后续的业务中需要openId的,由前端发送过来; ### **(2)举例** 定义一个**营业厅新版本**公众号应用菜单; 发布该菜单之后,底部显示了一个**营业厅新版本**菜单项; ![](https://img.kancloud.cn/64/28/6428c639155f02b538edfc26fe86ffca_325x626.png) 点击公众号中的**营业厅新版本**菜单项,就重定向到一个地址,且带上了当前用户的code了,然后调用微信api,获取openId,在前端登陆的时候,跟账户密码一起提交到后台,进行系统账号和微信账号的绑定了; ``` #假定我们设定的入口地址为http://weixinappurl/mobile http://weixinappurl/mobile?code=oNouq6QLIRKtdPaCM8Rv6p0-JQbs&state=appId ``` ### **(3)后续业务建议技术方案** 经上,获取openId,且绑定了业务系统账号和微信用户openId之后,后续业务开发,就不需要再传入openId了,直接转变为普通的H5开发了,只需要传入系统的rayAccessToken即可,在需要的时候,由后端提供兑换(基于rayAccessToken获取该用户的微信用户openId),也就是自动绑定了; > 如果万一需要解除这种绑定,则可以通过提供一个系统功能出来,让用户选择解除绑定,否则建议直接绑定;