1. 前言
微信小程序
開(kāi)發(fā)平臺(tái),提供有一類 API
,可以讓開(kāi)發(fā)者獲取到微信登錄用戶的個(gè)人數(shù)據(jù)。這類 API
統(tǒng)稱為開(kāi)放接口
。
Tip:微信小程序開(kāi)發(fā)平臺(tái),會(huì)把微信登錄用戶的個(gè)人信息分為明文數(shù)據(jù)和敏感數(shù)據(jù)。
明文數(shù)據(jù)也稱為公開(kāi)數(shù)據(jù),開(kāi)發(fā)者可以直接獲取到,如登錄者的昵稱、頭像……
敏感數(shù)據(jù)如電話號(hào)碼、唯一標(biāo)識(shí)符……等數(shù)據(jù),只有高級(jí)認(rèn)證開(kāi)發(fā)者和經(jīng)過(guò)登錄者授權(quán)后才能解密獲取到。
這一類 API
較多,且 API
之間功能有重疊之處,相互之間的區(qū)別較微小。有的適用于低版本,有的適用于高版本。
為了避免在使用時(shí)出現(xiàn)選擇混亂,本文將通過(guò)具體應(yīng)用案例介紹幾個(gè)常用 API
的使用。
2. 開(kāi)放接口
開(kāi)放接口
是對(duì)一類 API
的統(tǒng)稱,開(kāi)發(fā)者
可以通過(guò)調(diào)用這類接口得到微信登錄用戶的授權(quán)
或獲取登錄者的個(gè)人數(shù)據(jù)
。開(kāi)放接口
又分成幾個(gè)子類 API
:
- 登錄接口: 包括
wx.pluginLogin(Object args)
、wx.login(Object object)
、wx.checkSession(Object object)
幾 個(gè)API
。 - 賬號(hào)信息: 包括
Object wx.getAccountInfoSync()
此接口用來(lái)獲取開(kāi)發(fā)者的賬號(hào)信息。 - 用戶信息: 包括
wx.getUserProfile(Object object)
、wx.getUserInfo(Object object)
、UserInfo
。使用頻率非常高的接口,常用于小程序中獲取登錄者個(gè)人公開(kāi)數(shù)據(jù)。 - 授權(quán)接口:
wx.authorizeForMiniProgram(Object object)
、wx.authorize(Object object)
除上述列出的子類接口,還有收貨地址、生物認(rèn)證……等諸多子類 API
,有興趣者可以自行了解。
2.1 登錄接口
登錄
接口中有 3
個(gè) API
,對(duì)于開(kāi)發(fā)者來(lái)說(shuō),使用頻率較高的是 login
接口,此環(huán)節(jié)將重點(diǎn)介紹此接口。
非本文特別關(guān)注的接口,會(huì)簡(jiǎn)略帶過(guò)。
wx.pluginLogin(Object args)
:此接口只能在插件中可以調(diào)用,調(diào)用此接口獲得插件用戶的標(biāo)志憑證code
,插件可使用此憑證換取用于識(shí)別用戶的唯一標(biāo)識(shí) OpenpId
。
用戶不同、宿主小程序不同或插件不同的情況下,該標(biāo)識(shí)均不相同,即當(dāng)且僅當(dāng)同一個(gè)用戶在同一個(gè)宿主小程序中使用同一個(gè)插件時(shí),OpenpId
才會(huì)相同。
對(duì)于一般開(kāi)發(fā)者,此 接口用的不是很多,具體使用細(xì)節(jié)在此處也不做過(guò)多復(fù)述。
什么是 OpenId
?
當(dāng)微信用戶登錄公眾號(hào)或小程序時(shí),微信平臺(tái)為每一個(gè)微信登錄者分配的一個(gè)唯一標(biāo)識(shí)符號(hào)。
2.1.1 wx.login(Object object)
功能描述:
開(kāi)發(fā)者使用此接口可以獲取到微信登錄者
的登錄憑證(code)
。
登錄憑證
具有臨時(shí)性,也就是每次調(diào)用時(shí)都會(huì)不一樣,所以code
只能使用一次。
開(kāi)發(fā)者可以通過(guò)臨時(shí)code
,再向微信接口服務(wù)器索取登錄者的唯一標(biāo)識(shí)符 OpenId
、微信開(kāi)發(fā)平臺(tái)賬號(hào)的唯一標(biāo)識(shí) UnionID
(需要當(dāng)前小程序已綁定到微信開(kāi)放平臺(tái)帳號(hào))、以及會(huì)話密鑰 session_key
。
那么,獲取到的openId
和session_key
對(duì)于開(kāi)發(fā)者而言,有什么實(shí)質(zhì)性的意義?
- 根據(jù)
OpenId
的唯一性特點(diǎn),可以在微信用戶第一次登錄時(shí),把OpenID
保存在數(shù)據(jù)庫(kù)或緩存中,在后續(xù)登錄時(shí),只需要檢查用戶的OpenId
是否存在于數(shù)據(jù)庫(kù)或緩存中,便能實(shí)現(xiàn)自動(dòng)登錄功能。 session_key
也稱會(huì)話密鑰,用來(lái)解密微信登錄者的敏感數(shù)據(jù)。
后文將詳細(xì)介紹。
如何獲取OpenId
?
現(xiàn)通過(guò)一個(gè)簡(jiǎn)單案例,實(shí)現(xiàn)微信小程序端與開(kāi)發(fā)者服務(wù)器之間的數(shù)據(jù)交互。以此了解開(kāi)發(fā)者服務(wù)器如何通過(guò)微信小程序傳遞過(guò)來(lái)的用戶臨時(shí) code
換取到登錄者的更多信息。
實(shí)現(xiàn)之前,先通過(guò)一個(gè)簡(jiǎn)易演示圖了解其過(guò)程。
簡(jiǎn)單描述整個(gè)請(qǐng)求過(guò)程:
- 微信用戶打開(kāi)微信小程序后,開(kāi)發(fā)者在微信小程序中通過(guò)調(diào)用
wx.login
接口獲取到臨時(shí)登錄憑證code
。 - 在微信小程序中調(diào)用
wx.request
接口向開(kāi)發(fā)者服務(wù)器發(fā)送http
請(qǐng)求,需要把登錄憑證code
一并發(fā)送過(guò)去。 - 開(kāi)發(fā)者服務(wù)器使用發(fā)送過(guò)來(lái)的
code
以及開(kāi)發(fā)者憑證信息向微信接口服務(wù)器
索取微信登錄者的openId
和session_key
。
簡(jiǎn)而言之,就是 3
者(微信小程序、開(kāi)發(fā)者服務(wù)器、微信接口服務(wù)器)之間的一個(gè)擊鼓傳花游戲。
開(kāi)發(fā)流程:
第一步:項(xiàng)目結(jié)構(gòu)分析
完整的系統(tǒng)由 2
個(gè)部分組成:
微信小程序端 APP
。
如對(duì)微信小程序開(kāi)發(fā)不是很了解,請(qǐng)先閱讀官方提供的相關(guān)文檔。
服務(wù)器端應(yīng)用程序。
本文的服務(wù)器端應(yīng)用程序基于 Spring Boot
開(kāi)發(fā)平臺(tái)。
本項(xiàng)目結(jié)構(gòu)是標(biāo)準(zhǔn)的前后端分離模式,微信小程序是前端應(yīng)用,服務(wù)器端應(yīng)用程序?yàn)楹笈_(tái)應(yīng)用。
第二步:新建微信小程序(前端應(yīng)用)
打開(kāi)微信開(kāi)發(fā)工具,新建一個(gè)名為 guokeai
的小程序項(xiàng)目 ,項(xiàng)目會(huì)初始化一個(gè)index
頁(yè)面。在 index.js
中編寫(xiě)如下代碼。
//index.js
const app = getApp()
const httpRequest = require("../../utils/request.js")
Page({
data: {
isHasUserInfo: null,
userInfo: null
},
//啟動(dòng)時(shí)
onLoad: function () {
let this_ = this
/***
* 檢查微信用戶是否已經(jīng)登錄到后臺(tái)服務(wù)器
* 已經(jīng)登錄的標(biāo)志,數(shù)據(jù)庫(kù)中存在 OPENID
*/
let code = null
//調(diào)用 login 接口
wx.login({
success: (res) => {
//得到登錄用戶的臨時(shí) code
code = res.code
//向開(kāi)發(fā)者服務(wù)器發(fā)送請(qǐng)求
let api = "wx/getLoginCertificate"
let config = {
url: api,
method: "GET",
data: {
code: code
}
}
let promise = httpRequest.wxRequest(config)
promise.then(res => {
let isHas = null
// 有沒(méi)有完整的微信登錄者信息
isHas = res.data == 0 ? false : true
app.globalData.isHasUserInfo = isHas
this_.setData({
isHasUserInfo: isHas
})
}).catch(res => {
console.log("fail", res)
});
}
})
}
})
代碼解釋:
- 一般會(huì)在微信小程序啟動(dòng)時(shí),也就是在頁(yè)面
onload
函數(shù)中調(diào)用wx.login
接口,檢查用戶是否登錄過(guò)。 http://127.0.0.1:8080/wx/getLoginCertificate
是開(kāi)發(fā)者服務(wù)器
提供的對(duì)外處理微信用戶信息的接口。- 最后只是簡(jiǎn)單地輸出開(kāi)發(fā)者服務(wù)器端返回的數(shù)據(jù)。
httpRequest.wxRequest(config)
是自定義的封裝wx.request
接口的請(qǐng)求組件。
function wxRequest(config) {
//返回的數(shù)據(jù)類型
let dataType = config.dataType == null ? "json" : config.dataType;
let responseType = config.responseType == null ? "text" : config.responseType;
//服務(wù)器基地址
let serverUrl = "http://127.0.0.1:8080/"
//超時(shí)
let timeout = config.timeout == null ? 50000 : config.timeout;
//目標(biāo)地址,基地址+接口
let url = serverUrl + config.url;
//數(shù)據(jù)提交方式
let method = config.method == null ? "GET" : config.method;
//提交數(shù)據(jù)
let data = config.data == null ? null : config.data
//頭信息
let header = {
// 默認(rèn)值
'content-type': 'application/json',
'x-requested-with': 'XMLHttpRequest'
}
let sessionId = wx.getStorageSync('sessionId')
if (sessionId) {
header["cookie"] = sessionId
}
return new Promise(function (resolve, reject) {
wx.request({
url: url,
data: data,
//返回的數(shù)據(jù)類型(json)
dataType: dataType,
enableCache: false,
enableHttp2: false,
enableQuic: false,
method: method,
header: header,
responseType: responseType,
timeout: timeout,
success: (res) => {
console.log("requestData", res)
if (res.cookies != null && res.cookies.length != 0)
wx.setStorageSync('sessionId', res.cookies[0])
resolve(res)
},
fail: (res) => {
console.log("requestException", res)
reject(res)
}
})
})
}
第三步:創(chuàng)建開(kāi)發(fā)者服務(wù)器程序(后臺(tái)應(yīng)用)
本文使用 spring boot
快速搭建后臺(tái)應(yīng)用程序。在項(xiàng)目的 pom.xml
文件中除了必要的依賴包外,還需要添加以下 的依賴包。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
fastjson
是阿里云
提供的開(kāi)源 JSON
解析框架。
微信小程序
和開(kāi)發(fā)者服務(wù)器
構(gòu)建的項(xiàng)目結(jié)構(gòu),是標(biāo)準(zhǔn)的前后端分離模式。
請(qǐng)求與響應(yīng)時(shí),數(shù)據(jù)交互常使用JSON
格式。這時(shí)使用 fastjson
作為json
解析器,當(dāng)然,也可以選擇其它的類似解析器。
httpclient
是一個(gè)http
請(qǐng)求組件。mysql-connector-java
本文案例使用MySQL
數(shù)據(jù)庫(kù),需要加載相應(yīng)的驅(qū)動(dòng)包。mybatis-plus-boot-starter
,mybatis-plus
依賴包。
在后臺(tái)應(yīng)用中編寫(xiě)處理器(響應(yīng))組件:
@RestController
@RequestMapping("/wx")
public class WxAction {
@Autowired
private IWxService wxService;
/***
* 獲取到微信用戶的 OPENID
*/
@GetMapping("/getLoginCertificate")
public String getLoginCertificate(@RequestParam("code") String code) throws Exception {
WxUserInfo wxInfo = this.wxService.getLoginCertificate(code);
//用戶不存在,或者用戶的信息不全
return wxInfo==null || wxInfo.getNickName()==null?"0":"1";
}
代碼解釋:
IWxService
是處理器依賴的業(yè)務(wù)組件,提供有 getLoginCertificate()
方法用來(lái)實(shí)現(xiàn)通過(guò)code
向微信接口服務(wù)器
換取微信登錄者的 openId
和session_key
。
編寫(xiě)業(yè)務(wù)組件:
@Service
public class WxService implements IWxService {
@Override
public WxUserInfo getLoginCertificate(String code) throws Exception {
//請(qǐng)求地址
String requestUrl = WxUtil.getWxServerUrl(code);
// 發(fā)送請(qǐng)求
String response = HttpClientUtils.getRequest(requestUrl);
//格式化JSON數(shù)據(jù)
WxUserInfo wxUserInfo = JSONObject.parseObject(response, WxUserInfo.class);
//檢查數(shù)據(jù)庫(kù)中是否存在 OPENID
WxUserInfo wxUserInfo_ = this.wxUserMapper.selectById(wxUserInfo.getOpenId());
if (wxUserInfo_ == null) {
//數(shù)據(jù)庫(kù)中沒(méi)有用戶的 OPENID,添加到數(shù)據(jù)庫(kù)中
this.wxUserMapper.insert(wxUserInfo);
} else {
if (!wxUserInfo.getSessionKey().equals(wxUserInfo_.getSessionKey())) {
//如果數(shù)據(jù)庫(kù)保存的session_key和最新的session_key 不相同,則更新
wxUserInfo_.setSessionKey(wxUserInfo.getSessionKey());
this.wxUserMapper.updateById(wxUserInfo_);
}
}
return wxUserInfo_;
}
}
代碼解釋:
WxUtil
是自定義的一個(gè)工具組件,用來(lái)構(gòu)建請(qǐng)求微信接口服務(wù)器
的url
。
https://api.weixin.qq.com/sns/jscode2session
是微信接口服務(wù)器
對(duì)外提供的接口,請(qǐng)求此接口時(shí),需要提供 4
個(gè)請(qǐng)求數(shù)據(jù)。
appid
:小程序 appId。
secret
:小程序 appSecret。
js_code
:獲取到的微信登錄者的臨時(shí) code
。
grant_type
:授權(quán)類型,此處只需填寫(xiě) authorization_code
。
public class WxUtil {
private final static String APP_ID = "微信小程序開(kāi)發(fā)者申請(qǐng)的 appid";
private final static String APP_SECRET = "微信小程序開(kāi)發(fā)者申請(qǐng)的 APP_SECRET";
//
private final static String WX_LOGIN_SERVER_URL = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code";
public static String getWxServerUrl(String code) throws IOException {
String url = MessageFormat.format(WX_LOGIN_SERVER_URL, new String[]{APP_ID, APP_SECRET, code});
return url;
}
}
HttpClientUtils
也是一個(gè)自定義組件,用來(lái)向指定的服務(wù)器發(fā)送 http
請(qǐng)求。
public class HttpClientUtils {
/**
* GET請(qǐng)求
*/
public static String getRequest(String url) throws Exception {
//HttpClient對(duì)象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
try {
HttpGet httpGet = new HttpGet(url);
response = httpClient.execute(httpGet);
//響應(yīng)體
HttpEntity entity = response.getEntity();
if (entity != null) {
//格式化響應(yīng)體
return EntityUtils.toString(entity);
}
} catch (ClientProtocolException e) {
throw e;
} catch (IOException e) {
throw e;
} finally {
response.close();
httpClient.close();
}
return null;
}
}
WxUserInfo
是自定義的數(shù)據(jù)封裝類。微信接口服務(wù)器
返回的數(shù)據(jù)是以JSON
格式組裝的,這里需要格式成對(duì)象數(shù)據(jù),便于在 java
中處理。本文使用 MyBatisPlus
操作數(shù)據(jù)庫(kù),此類也對(duì)應(yīng)數(shù)據(jù)庫(kù)中的gk_wx_user
表。
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("gk_wx_user")
public class WxUserInfo {
//OPEN_id
@TableId(type = IdType.ASSIGN_ID, value = "open_id")
private String openId;
//會(huì)話密鑰
@TableField(value = "session_key")
private String sessionKey;
//頭像路徑
@TableField("avatar_url")
private String avatarUrl;
//城市
private String city;
//國(guó)家
private String country;
//性別
private String gender;
//語(yǔ)言
private String language;
//昵稱
@TableField("nick_name")
private String nickName;
//備注名或真實(shí)名
@TableField("real_name")
private String realName;
//省份
private String province;
//學(xué)生ID
@TableField("stu_id")
private Integer stuId;
}
MyBatis 數(shù)據(jù)庫(kù)映射組件:
@Repository
public interface WxUserMapper extends BaseMapper<WxUserInfo> {
}
第四步:測(cè)試。
先啟動(dòng)后臺(tái)應(yīng)用程序,再啟動(dòng)微信小程序,可以在數(shù)據(jù)庫(kù)表中查看到如下信息。
微信用戶的openid
和session_key
已經(jīng)保存到后臺(tái)的數(shù)據(jù)庫(kù)表中。
2.1.2 wx.checkSession(Object object)
官方文檔中,有一段對(duì) session_key
的生命周期的描述。
session_key
的生命周期有不確定性,可以使用wx.login
接口刷新session_key
。為了避免頻繁調(diào)用wx.login
接口,可以通過(guò)調(diào)用wx.checkSession(Object object)
接口判斷session_key
是否已經(jīng)過(guò)期。- 當(dāng)開(kāi)發(fā)者在實(shí)現(xiàn)自定義登錄態(tài)時(shí),可以考慮以
session_key
有效期作為自身登錄態(tài)有效期,也可以實(shí)現(xiàn)自定義的時(shí)效性策略。
wx.checkSession
的功能,可以使用此接口判斷session_key
是否過(guò)期。
- 調(diào)用成功說(shuō)明當(dāng)前
session_key
未過(guò)期。 - 調(diào)用失敗說(shuō)明
session_key
已過(guò)期。
2.2 用戶信息接口
wx.login
接口僅能獲取到微信登錄者的有限數(shù)據(jù),如果想要獲取到登錄者的更多個(gè)人信息,可以使用用戶信息接口中的相關(guān)API
。
wx.getUserProfile(Object object)
。獲取用戶信息,頁(yè)面產(chǎn)生點(diǎn)擊事件(例如button
上bindtap
的回調(diào)中)后才可調(diào)用,每次請(qǐng)求都會(huì)彈出授權(quán)窗口,用戶同意后返回userInfo
。wx.getUserInfo(Object object)
。和wx.getUserProfile
的功能一樣,在基礎(chǔ)庫(kù) 2.10 的后續(xù)版本中,其功能已經(jīng)被削弱。UserInfo
是用戶信息封裝類。
getUserProfile
是從 基礎(chǔ)庫(kù)2.10.4
版本開(kāi)始支持的接口,該接口用來(lái)替換 wx.getUserInfo
,意味著官方不建議再使用getUserInfo
接口獲取用戶的個(gè)人信息。
下圖是官方提供的 2
個(gè)接口的功能對(duì)比圖。
為了避免頻繁彈窗,可以在第一次獲取到用戶信息后保存在數(shù)據(jù)庫(kù)中以備以后所用。為了獲取到用戶的敏感數(shù)據(jù),在后臺(tái)要通過(guò)getUserProfile
接口所獲取的數(shù)據(jù)進(jìn)行解密操作。
2.2.2 wx.getUserProfile
下面通過(guò)具體代碼講解如何保存微信登錄者的個(gè)人數(shù)據(jù)。先了解一下整個(gè)數(shù)據(jù)獲取的流程,這里直接截取官方提供的一張流程圖。
獲取微信登錄者的個(gè)人信息,需要經(jīng)過(guò) 2
個(gè)步驟。
簽名效驗(yàn):
- 通過(guò)調(diào)用
wx.getUserProfile
接口獲取數(shù)據(jù)時(shí),接口會(huì)同時(shí)返回rawData
、signature
,其中signature = sha1( rawData + session_key )
。 - 開(kāi)發(fā)者將
signature
、rawData
發(fā)送到開(kāi)發(fā)者服務(wù)器進(jìn)行校驗(yàn)。服務(wù)器利用用戶對(duì)應(yīng)的session_key
使用相同的算法計(jì)算出簽名signature2
,比對(duì)signature
與signature2
即可校驗(yàn)數(shù)據(jù)的完整性。
解密加密數(shù)據(jù):
- 對(duì)稱解密使用的算法為
AES-128-CBC
,數(shù)據(jù)采用PKCS#7
填充。 - 對(duì)稱解密的目標(biāo)密文為
Base64_Decode(encryptedData)
。 - 對(duì)稱解密秘鑰
aeskey = Base64_Decode(session_key)
,aeskey
是16
字節(jié)。 - 對(duì)稱解密算法初始向量 為
Base64_Decode(iv)
,其中iv
由數(shù)據(jù)接口返回。
具體編寫(xiě)實(shí)現(xiàn)。
第一步:在微信小程序端編碼。
在index.wxml
頁(yè)面中添加一個(gè)按鈕,并注冊(cè)bindtap
事件。
<view>
<button bindtap="getUserProfile">獲取用戶數(shù)據(jù)</button>
</view>
在index.js
中添加一個(gè)名為getUserProfile
的事件回調(diào)函數(shù)。為了避免不必要的彈窗,只有當(dāng)后臺(tái)沒(méi)有獲取到個(gè)人數(shù)據(jù)時(shí),才調(diào)用wx.getUserProfile
接口。
getUserProfile: function (e) {
let this_ = this
if (!this.data.isHasUserInfo) {
//如果服務(wù)器端沒(méi)有保存完整的微信登錄者信息
wx.getUserProfile({
desc: '需要完善您的資料!',
success: (res) => {
this_.setData({
//小程序中用來(lái)顯示個(gè)人信息
userInfo: res.userInfo,
isHasUserInfo: true
})
//再次登錄,因?yàn)?session_key 有生命中周期
wx.login({
success(res_) {
//保存到服務(wù)器端
let config = {
url: "wx/wxLogin",
method: "GET",
data: {
code: res_.code,
//明文數(shù)據(jù)
rawData: res.rawData,
//加密數(shù)據(jù)
encryptedData: res.encryptedData,
iv: res.iv,
//數(shù)字簽名
signature: res.signature
}
}
let promise = httpRequest.wxRequest(config)
promise.then(res => {
//返回
console.log("wxLogin", res)
}).catch(res => {
console.log("fail", res)
});
}
})
}
})
}
}
服務(wù)器端代碼:
在pom.xml
文件中添加如下依賴包,用來(lái)解密數(shù)據(jù)。
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
<version>1.46</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
在處理器類WxAction
中添加wxLogin
響應(yīng)方法。
@RestController
@RequestMapping("/wx")
public class WxAction {
@Autowired
private IWxService wxService;
/***
*
* @param code
* @param rawData
* @param encryptedData
* @param iv
* @param signature
* @return
* @throws Exception
*/
@GetMapping("/wxLogin")
public WxUserInfo wxLogin(@RequestParam("code") String code, @RequestParam("rawData") String rawData,
@RequestParam("encryptedData") String encryptedData, @RequestParam("iv") String iv,
@RequestParam("signature") String signature) throws Exception {
WxUserInfo wxInfo = this.wxService.getWxUserInfo(code, rawData, encryptedData, iv, signature);
return wxInfo;
}
}
業(yè)務(wù)代碼:
小程序中傳遞過(guò)來(lái)的數(shù)據(jù)是經(jīng)過(guò)base64
編碼以及加密的數(shù)據(jù),需要使用 Base64
解碼字符串,再使用解密算法解密數(shù)據(jù)。先提供一個(gè)解密方法。
public String decrypt(String session_key, String iv, String encryptData) {
String decryptString = "";
//解碼經(jīng)過(guò) base64 編碼的字符串
byte[] sessionKeyByte = Base64.getDecoder().decode(session_key);
byte[] ivByte = Base64.getDecoder().decode(iv);
byte[] encryptDataByte = Base64.getDecoder().decode(encryptData);
try {
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
//得到密鑰
Key key = new SecretKeySpec(sessionKeyByte, "AES");
//AES 加密算法
AlgorithmParameters algorithmParameters = AlgorithmParameters.getInstance("AES");
algorithmParameters.init(new IvParameterSpec(ivByte));
cipher.init(Cipher.DECRYPT_MODE, key, algorithmParameters);
byte[] bytes = cipher.doFinal(encryptDataByte);
decryptString = new String(bytes);
} catch (Exception e) {
e.printStackTrace();
}
return decryptString;
}
具體獲取數(shù)據(jù)的業(yè)務(wù)實(shí)現(xiàn):
@Override
public WxUserInfo getWxUserInfo(@NotNull String code, @NotNull String rawData, @NotNull String encryptedData, @NotNull String iv, @NotNull String signature) throws Exception {
//會(huì)話密鑰
WxUserInfo wxUserInfo = this.getLoginCertificate(code);
String signature2 = DigestUtils.sha1Hex(rawData + wxUserInfo.getSessionKey());
if (!signature.equals(signature2)) {
throw new Exception("數(shù)字簽名驗(yàn)證失敗");
}
//數(shù)字簽名驗(yàn)證成功,解密
String infos = this.decrypt(wxUserInfo.getSessionKey(), iv, encryptedData);
//反序列化 JSON 數(shù)據(jù)
WxUserInfo wxUserInfo_ = JSONObject.parseObject(infos, WxUserInfo.class);
wxUserInfo_.setSessionKey(wxUserInfo.getSessionKey());
wxUserInfo_.setOpenId(wxUserInfo.getOpenId());
//更新數(shù)據(jù)庫(kù)
this.wxUserMapper.updateById(wxUserInfo_);
return wxUserInfo_;
}
測(cè)試,啟動(dòng)微信小程序和后臺(tái)應(yīng)用,在小程序中觸發(fā)按鈕事件。
在彈出的對(duì)話框中,選擇允許。
查看后臺(tái)數(shù)據(jù)庫(kù)表中的數(shù)據(jù)。
能夠獲取到的微信登錄者個(gè)人信息都保存到了數(shù)據(jù)庫(kù)表中。至于怎么使用這些數(shù)據(jù),可以根據(jù)自己的業(yè)務(wù)需要定制。
3.總結(jié)
微信開(kāi)發(fā)平臺(tái),提供有諸多接口,可以幫助開(kāi)發(fā)者獲取到有用的數(shù)據(jù)。本文主要介紹 wx.login
和wx.getProfile
接口,因篇幅所限,不能對(duì)其它接口做詳細(xì)介紹 ,有興趣者可以查閱官方文檔。
官方文檔只會(huì)對(duì)接口功能做些介紹 ,如要靈活運(yùn)用這些接口,還需要結(jié)合實(shí)際需要演練一下,如此方能有切身體會(huì)。
到此這篇關(guān)于Spring Boot+微信小程序開(kāi)發(fā)平臺(tái)保存微信登錄者的個(gè)人信息的文章就介紹到這了,更多相關(guān)springboot微信小程序內(nèi)容請(qǐng)搜索html5模板網(wǎng)以前的文章希望大家以后多多支持html5模板網(wǎng)!