## 一、概述
`OpenId`是一個用戶對于一個小程序/公眾號的標識,開發者可以通過這個標識識別出用戶,比如微信支付的很多產品,都是通過openId來確定支付者的身份;
`UnionId`是一個用戶對于同主體微信小程序/公眾號/APP的標識,開發者需要在微信開放平臺下綁定相同賬號的主體。開發者可通過`UnionId`,實現多個小程序、公眾號、甚至APP 之間的數據互通了;
## 二、技術方案
### **技術原理**
(1)用戶在微信客戶端中訪問我們開發的網頁,公眾號就可以通過微信網頁授權機制,來獲取用戶基本信息,進而實現業務邏輯;
前提:在微信公眾號請求用戶網頁授權之前,開發者需要先到公眾平臺官網中的“開發 - 接口權限 - 網頁服務 - 網頁帳號 - 網頁授權獲取用戶基本信息”的配置選項中,修改授權回調域名。請注意,這里填寫的是域名(是一個字符串),而不是URL,因此請勿加 http:// 等協議頭;

(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
```

為了生成的授權地址是正確的,必須確保服務器部署的地址配置正確,該配置項是外部訪問當前后端服務的根地址,供微信重定向給前端的時候回調到當前后端服務器;
```
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;
}
```

## 四、從自定義菜單跳轉獲取
### **(1)實現步驟**
首先、假定一個入口地址,在公眾號的底部菜單配置的時候,把當前入口地址包裹oauth2授權之后的地址作為菜單URL發布;
然后、從微信中訪問公眾號,點擊該菜單,就會在剛才定義的入口地址后面,追加上微信用戶的code了;
最后、前端獲取到的code,然后再結合appId和appSecret,即可取得openId;可以跟后端接口進行業務整合了;
>[danger] 比如:可以跟登陸的賬號信息一起提交過來,后臺可以立即將系統賬號與微信用戶的openId進行綁定,后續業務將自動從后端關聯獲取openId;也可以不綁定,僅僅讓前端持有,那么,后續的業務中需要openId的,由前端發送過來;
### **(2)舉例**
定義一個**營業廳新版本**公眾號應用菜單;
發布該菜單之后,底部顯示了一個**營業廳新版本**菜單項;

點擊公眾號中的**營業廳新版本**菜單項,就重定向到一個地址,且帶上了當前用戶的code了,然后調用微信api,獲取openId,在前端登陸的時候,跟賬戶密碼一起提交到后臺,進行系統賬號和微信賬號的綁定了;
```
#假定我們設定的入口地址為http://weixinappurl/mobile
http://weixinappurl/mobile?code=oNouq6QLIRKtdPaCM8Rv6p0-JQbs&state=appId
```
### **(3)后續業務建議技術方案**
經上,獲取openId,且綁定了業務系統賬號和微信用戶openId之后,后續業務開發,就不需要再傳入openId了,直接轉變為普通的H5開發了,只需要傳入系統的rayAccessToken即可,在需要的時候,由后端提供兌換(基于rayAccessToken獲取該用戶的微信用戶openId),也就是自動綁定了;
> 如果萬一需要解除這種綁定,則可以通過提供一個系統功能出來,讓用戶選擇解除綁定,否則建議直接綁定;
- 引言
- 01、開發工具
- Maven
- 術語
- 倉庫
- Archetype
- 安裝配置
- 典型配置
- 內置變量
- eclipse插件
- 本地包安裝
- 依賴庫更新
- 依賴庫排錯
- 常見問題
- Gradle
- build.gradle
- gradle插件
- eclipse插件
- Eclipse
- json生成bean
- 常見問題
- IDEA Community
- 工程管理
- maven操作
- 格式化
- 常見問題
- Git
- GitHub
- 快速開始
- 既有工程
- 新建工程
- 日常提交
- PR操作
- 多人協作
- 常用命令
- 常見問題
- 同步代碼
- 發布庫包
- CodeGenerator
- VSCode
- 安裝
- 配置
- 快速開始
- 與GitHub整合
- 斷點調試
- 便捷開發
- 擴展
- prettier+
- Vetur
- 前端調試
- F12調試工具
- Vue前端調試
- 測試工具
- 壓力測試
- 接口測試
- 抓包工具
- 導入證書
- SecureCRT
- 02、前端技術
- 前端設計
- javascript
- 基本語法
- 數據類型
- 類型轉換
- 錯誤處理
- console對象
- 標準庫
- 異步操作
- ES6及后續增強
- 模塊化
- 擴展運算符
- 解構變量
- 箭頭函數
- 混入模式
- web標準
- css
- html
- HistoryApi
- dom
- 如何理解
- 虛擬dom
- JSON
- svg
- WebAssembly
- web components
- HtmlComponents
- Custom Elements
- 標準擴展
- javascript
- Babel
- TypeScript
- JavaScript
- ECMAScript
- 模塊化
- CommonJS
- require
- exports與module.exports
- ES6模塊
- export
- import
- AMD
- define
- require
- CMD
- define
- require
- Web Storage
- JSX
- ES6語法
- 語法糖
- ==和===
- let與const
- call&apply
- 內置對象
- Object
- Class
- Promise對象
- then
- catch
- finally
- resolve
- reject
- Module
- Generator函數
- arguments
- 函數擴展
- 數組
- 對象
- Set和Map
- Proxy對象
- css
- sass
- less
- postcss
- CSS Modules
- Node.js
- 安裝
- npm
- ls
- init
- install
- run
- uninstall
- update
- version
- npm生態
- yarn
- package.json
- node_modules
- 常用技術
- 應用實例
- Web框架
- Express
- Egg.js
- Mock
- Mock.js
- 語法規范
- 非核心api
- 核心api
- easymock
- 開發測試
- ESLint
- jest
- Travis
- Prettier
- stylelint
- 構建工具
- gulp
- Browserify
- webpack
- 安裝配置
- 入口起點entry
- 輸出output
- 裝載器loader
- 插件plugins
- webpack-cli
- public目錄
- 技術概念
- CSR與SSR
- polyfill
- axios
- 請求對象
- 響應對象
- 自定義實例
- 攔截器
- 跨域訪問
- 03、前端框架
- mvvm
- vue.js
- 簡明指南
- vue文件結構
- 組件指南
- 組件命名
- 應用流程
- 單文件組件
- 組件導入導出
- 生命周期
- Prop
- 復用方法
- 懶加載
- 全局環境
- 全局配置
- 全局API
- 選項對象
- 混入選項
- vue實例$
- vue指令
- v-bind(:)
- v-on(@)
- v-model
- 特殊屬性
- 內置組件
- 自定義機制
- 組件
- 指令
- 過濾器
- 混入
- slot插槽
- 渲染函數
- 注意事項
- 總結
- vueCli
- 安裝
- 組成部分
- vue.config.js
- vue核心文件
- 狀態管理
- 簡單狀態
- Vuex
- 構造器選項
- 實例屬性
- 實例方法
- 綁定輔助函數
- 模塊化
- 總結
- 路由管理
- 簡單路由
- Vue Router
- 路由模式
- route
- router
- <router-link>與編程式
- <router-view>
- 嵌套路由
- 導航守衛
- 總結
- vue插件
- Vue Loader
- 實戰舉例
- vue快速入門
- vue與后臺聯動
- vue完整實例
- vue組件庫
- vue-ls
- Enquire.js
- lodash
- md5.js
- moment
- nprogress
- viser-vue
- vue-clipboard2
- vue-cropper
- vue-quill-editor
- wangeditor
- vue-svg-icon-loader
- 實戰參考
- Vue Antd Admin
- ant-design-vue
- 快速開始
- 要點解析
- vuepress
- vant
- 04、后端框架
- SprigBoot
- 快速入門
- 完整示例
- 完整進階
- 核心技術
- 核心標記
- 頁面技術
- Thymeleaf
- 數據訪問
- 基本用法
- 事務控制
- 事務規則
- 注意事項
- 實體狀態
- 數據查詢
- 普通查詢
- 分頁查詢
- 統計查詢
- 命名訪問
- 公用共享
- 緩存機制
- 服務層
- 控制器
- AOP
- 定時任務
- 異步任務
- 靜態注入
- WebClient
- 啟動機制
- 應用監控
- 線程安全
- 調試測試
- 打包部署
- 打jar包
- 常見問題
- 配置問題
- 開發問題
- 文檔生成
- 相關技術
- springfox
- knife4j
- actuator
- kaptcha
- YAML
- API Blueprint
- 啟用https
- SpringSecurity
- 快速入門
- 核心元素
- jwt
- 與springsecurity集成
- 05、運行容器
- artemis
- 協議支持
- mqtt
- 安裝運行
- 管理配置
- 日志配置
- 業務配置
- 安全配置
- 數據存儲
- SSL支持
- 運行維護
- mosquitto
- 安裝運行
- 管理配置
- SSL支持
- rocketmq
- 安裝運行
- 控制臺
- 代碼實例
- kafka
- ZooKeeper
- 安裝運行
- 代碼實例
- zookeeper
- 安裝運行
- 應用實例
- dubbo
- 代碼實例
- hadoop
- 安裝配置
- 快速運行
- netty
- 06、相關技術
- Serverless
- Protobuf
- SSL
- 證書
- 認證類型
- 硬件技術
- 基礎知識
- 開發技術
- 消息協議
- 07、項目實戰
- 前端開發
- 從零開始開發
- 開發環境搭建
- 原生技術開發
- 路由守衛
- 動態路由菜單
- 全局API
- 登錄認證
- 與后端交互
- 代碼開發調試
- 快速打包發布
- 常見問題收集
- 后端開發
- 從零開始開發
- 開發環境搭建
- 常用注解說明
- 常用基礎設施
- 核心業務約定
- 平臺配置文件
- 業務配置清單
- 關鍵配置參數
- 項目必配參數
- 項目調優參數
- 返回結果處理
- 字段翻譯機制
- 列表字段翻譯
- 實體字段翻譯
- 組合字段翻譯
- 列表數據增強
- 列表數據簡化
- 返回字段過濾
- 返回字段改名
- 定制返回結果
- 原生技術開發
- 動態級聯字典
- 簡單數據查詢
- 短信驗證業務
- 測試數據模擬
- 開放平臺登陸
- 微信開放平臺
- 抖音開放平臺
- 文件處理方案
- 文件字段存儲
- 文件字段解析
- 圖像數據存取
- 文件資源方案
- 服務集成開發
- redis服務集成
- mqtt服務集成
- kafka集成
- rocketmq集成
- websocket集成
- elasticsearch集成
- netty集成
- 外部工具開發
- 發送短信服務
- 發送郵件服務
- 動態pdf生成
- 數據處理開發
- 同步導出數據
- 異步導出數據
- 同步導入數據
- 異步導入數據
- 多線程與并發
- 線程并發安全
- 操作間隔控制
- 異步待辦機制
- 平臺定時任務
- 平臺異步任務
- 常見注意事項
- 安全相關開發
- 接口安全策略
- 接口限流策略
- 接口授權策略
- 權限相關開發
- 路由權限方案
- 組織權限方案
- 數據權限方案
- 字段權限方案
- 按鈕權限方案
- 支付相關開發
- 微信原生支付
- 微信H5支付
- 微信JSAPI支付
- 微信批量轉賬
- 微信動態支付
- 支付寶移動網站支付
- 支付寶PC網站支付
- 平臺緩存機制
- 內置進程內緩存
- 內置分布式緩存
- 平臺自定義緩存
- 平臺插件機制
- 賬號的邀請碼
- 賬號的二維碼
- 定制事件機制
- 約定實現機制
- 請求回調機制
- 啟動自動加載
- 平臺基礎設施
- 動態參數加載
- 定制待定常量
- 定制單位組織
- 平臺緩存機制
- 平臺外訪機制
- 靜態資源獲取
- 調試打印機制
- 數據源隨時用
- 上下文隨處拿
- 平臺診斷機制
- 平臺內置資源
- 強制間隔時間
- 賬號擴展開發
- 賬號變更事件
- 業務開發指南
- 字典數據獲取
- 數據層持久化
- 基礎服務調用
- 查詢時間范圍
- 代碼開發調試
- 常見問題收集
- 從零開始
- PCV1運行
- PCV2運行
- H5端運行
- 開發進階
- 最佳實踐
- 開發方案
- 前后分離
- 跨域訪問
- 庫表設計
- 模型設計
- 容器部署
- 集群部署
- 日志收集
- 動態配置
- 開發管理
- 開發環境
- 代碼控制
- 問題跟蹤
- 進度跟蹤
- 測試環境
- 調試輔助
- DevOps
- 代碼風格
- 運行維護
- 基本監控知識
- 線程堆棧分析
- 內存堆棧分析
- 應用診斷工具
- 工程示范
- 后端開發
- 前端開發
- PC端
- 移動端
- 08、內置容器
- 調度服務
- 調度容器
- 快速開發
- 線程并發
- 多點部署
- 本地調試
- 常見問題
- 開放服務
- 快速接入
- 接口開發
- 09、開放平臺
- 微信公號
- 環境準備
- 環境配置
- 技術方案
- 獲取OpenId
- 常見問題
- 10、平臺功能
- 系統管理
- 單位組織
- 角色管理
- 賬號管理
- 子賬號
- 財務賬戶
- 開放數據
- 綁定數據
- 套餐權益
- 會員定義
- 變更審核
- 注冊審核
- 系統配置
- 路由配置
- 參數配置
- 屬性配置
- 樹形設置
- 服務接口
- 訪問設置
- 系統監控
- 在線用戶
- 內存數據
- 系統變量
- 外訪數據
- 到訪數據
- 操作記錄
- 靜態字典
- 日志管理
- 元數據
- 接入管理
- 微信公號
- 微信支付
- 開放服務
- 客戶端
- 服務列表
- 請求歷史
- 請求服務
- 調度服務
- 調度監控
- 11、補充語言
- php
- 生產環境
- 安裝
- 初始配置
- nginx集成
- 配置文件
- 語法
- 變量和常量
- 數據類型
- 條件控制
- 運算符
- 數組
- 指針
- 循環控制
- 函數
- 語法糖
- 預定義變量
- session和cookie
- 命名空間
- 面向對象
- 數據庫操作
- 表單
- 錯誤
- 異常
- 過濾器
- JSON
- XML
- AJAX
- Composer
- 開發環境
- 本地調試
- 遠程調試
- .net
- 開發環境
- C#快速入門
- 12、依賴容器
- elasticsearch
- 運行配置
- 命令操作
- 中文分詞
- Kibana
- Logstash
- 開發技術
- 搜索類型
- 代碼示例
- 應用場景
- 常見問題
- nginx
- 下載安裝
- 基本配置
- 服務啟停
- 安全防護
- 常見問題
- linux
- 常用操作
- 常用命令
- 用戶管理
- ftp服務
- 防火墻
- 運維
- 網絡安全
- 內核參數
- 安裝
- yum源問題
- mysql
- 安裝配置
- 快速安裝
- 正式安裝
- 參數配置
- 性能優化
- 語句優化
- 配置優化
- 設計優化
- 運維常識
- 系統監控
- 連接數
- 超時
- cpu利用率
- 數據備份
- 導入復制
- 經驗舉例
- 故障處理
- 用戶管理
- 系統日志
- 日志清理
- 安全經驗
- 集群方案
- MySQL Replication
- MySQL Cluster
- 常見問題
- redis
- 安裝配置
- 安裝運行
- 參數配置
- 運維常識
- 技術要點
- pubSub
- 操作命令
- 持久化
- 常見問題
- docker
- 安裝運行
- 鏡像操作
- 容器操作
- 倉庫操作
- 實戰案例
- kubernetes
- 后記