如何使用Spring Boot实现微信小程序中保存登录者个人信息的功能?
- 内容介绍
- 相关推荐
本文共计4416个文字,预计阅读时间需要18分钟。
1.+ 微信小程序 + 开发平台,提供一类API,开发者可获取微信登录用户的个人数据。这类API通常称为开放接口。+ 提示:微信小程序开发平台会将微信登录用户的个人信息分发给开发者。
1. 前言微信小程序开发平台,提供有一类 API,可以让开发者获取到微信登录用户的个人数据。这类 API 统称为开放接口。
Tip:微信小程序开发平台,会把微信登录用户的个人信息分为明文数据和敏感数据。
明文数据也称为公开数据,开发者可以直接获取到,如登录者的昵称、头像……
敏感数据如电话号码、唯一标识符……等数据,只有高级认证开发者和经过登录者授权后才能解密获取到。
这一类 API较多,且 API之间功能有重叠之处,相互之间的区别较微小。有的适用于低版本,有的适用于高版本。
为了避免在使用时出现选择混乱,本文将通过具体应用案例介绍几个常用 API的使用。
开放接口是对一类 API的统称,开发者可以通过调用这类接口得到微信登录用户的授权或获取登录者的个人数据。
开放接口又分成几个子类 API :
- 登录接口: 包括
wx.pluginLogin(Object args)、wx.login(Object object)、wx.checkSession(Object object)几 个API。 - 账号信息: 包括
Object wx.getAccountInfoSync()此接口用来获取开发者的账号信息。 - 用户信息: 包括
wx.getUserProfile(Object object)、wx.getUserInfo(Object object)、UserInfo。使用频率非常高的接口,常用于小程序中获取登录者个人公开数据。 - 授权接口:
wx.authorizeForMiniProgram(Object object)、wx.authorize(Object object)
除上述列出的子类接口,还有收货地址、生物认证……等诸多子类 API,有兴趣者可以自行了解。
登录接口中有 3 个 API,对于开发者来说,使用频率较高的是 login接口,此环节将重点介绍此接口。
非本文特别关注的接口,会简略带过。
wx.pluginLogin(Object args):此接口只能在插件中可以调用,调用此接口获得插件用户的标志凭证code,插件可使用此凭证换取用于识别用户的唯一标识 OpenpId。
用户不同、宿主小程序不同或插件不同的情况下,该标识均不相同,即当且仅当同一个用户在同一个宿主小程序中使用同一个插件时,OpenpId 才会相同。
对于一般开发者,此 接口用的不是很多,具体使用细节在此处也不做过多复述。
2.1.1什么是
OpenId?当微信用户登录公众号或小程序时,微信平台为每一个微信登录者分配的一个唯一标识符号。
wx.login(Object object)
功能描述:
-
开发者使用此接口可以获取到
微信登录者的登录凭证(code)。登录凭证具有临时性,也就是每次调用时都会不一样,所以code只能使用一次。 -
开发者可以通过临时
code,再向微信接口服务器索取登录者的唯一标识符OpenId、微信开发平台账号的唯一标识UnionID(需要当前小程序已绑定到微信开放平台帐号)、以及会话密钥session_key。
那么,获取到的openId和session_key对于开发者而言,有什么实质性的意义?
-
根据
OpenId的唯一性特点,可以在微信用户第一次登录时,把OpenID保存在数据库或缓存中,在后续登录时,只需要检查用户的OpenId是否存在于数据库或缓存中,便能实现自动登录功能。 -
session_key也称会话密钥,用来解密微信登录者的敏感数据。后文将详细介绍。
如何获取OpenId?
现通过一个简单案例,实现微信小程序端与开发者服务器之间的数据交互。以此了解开发者服务器如何通过微信小程序传递过来的用户临时 code换取到登录者的更多信息。
实现之前,先通过一个简易演示图了解其过程。
简单描述整个请求过程:
- 微信用户打开微信小程序后,开发者在微信小程序中通过调用
wx.login接口获取到临时登录凭证code。 - 在微信小程序中调用
wx.request接口向开发者服务器发送127.0.0.1:8080/wx/getLoginCertificate是开发者服务器提供的对外处理微信用户信息的接口。 - 最后只是简单地输出开发者服务器端返回的数据。
127.0.0.1:8080/" //超时 let timeout = config.timeout == null ? 50000 : config.timeout; //目标地址,基地址+接口 let url = serverUrl + config.url; //数据提交方式 let method = config.method == null ? "GET" : config.method; //提交数据 let data = config.data == null ? null : config.data //头信息 let header = { // 默认值 '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, //返回的数据类型(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) } }) }) }第三步:创建开发者服务器程序(后台应用)
本文使用
spring boot快速搭建后台应用程序。在项目的pom.xml文件中除了必要的依赖包外,还需要添加以下 的依赖包。<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.73</version> </dependency> <dependency> <groupId>org.apache.api.weixin.qq.com/sns/jscode2session是微信接口服务器对外提供的接口,请求此接口时,需要提供4个请求数据。appid:小程序 appId。secret:小程序 appSecret。js_code:获取到的微信登录者的临时code。grant_type:授权类型,此处只需填写authorization_code。
public class WxUtil {
private final static String APP_ID = "微信小程序开发者申请的 appid";
private final static String APP_SECRET = "微信小程序开发者申请的 APP_SECRET";
//
private final static String WX_LOGIN_SERVER_URL = "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也是一个自定义组件,用来向指定的服务器发送http请求。
public class HttpClientUtils {
/**
* GET请求
*/
public static String getRequest(String url) throws Exception {
//HttpClient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
try {
HttpGet httpGet = new HttpGet(url);
response = httpClient.execute(httpGet);
//响应体
HttpEntity entity = response.getEntity();
if (entity != null) {
//格式化响应体
return EntityUtils.toString(entity);
}
} catch (ClientProtocolException e) {
throw e;
} catch (IOException e) {
throw e;
} finally {
response.close();
httpClient.close();
}
return null;
}
}
WxUserInfo是自定义的数据封装类。微信接口服务器返回的数据是以JSON格式组装的,这里需要格式成对象数据,便于在java中处理。本文使用MyBatisPlus操作数据库,此类也对应数据库中的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;
//会话密钥
@TableField(value = "session_key")
private String sessionKey;
//头像路径
@TableField("avatar_url")
private String avatarUrl;
//城市
private String city;
//国家
private String country;
//性别
private String gender;
//语言
private String language;
//昵称
@TableField("nick_name")
private String nickName;
//备注名或真实名
@TableField("real_name")
private String realName;
//省份
private String province;
//学生ID
@TableField("stu_id")
private Integer stuId;
}
MyBatis 数据库映射组件:
@Repository
public interface WxUserMapper extends BaseMapper<WxUserInfo> {
}
第四步:测试。
先启动后台应用程序,再启动微信小程序,可以在数据库表中查看到如下信息。
微信用户的openid和session_key已经保存到后台的数据库表中。
wx.checkSession(Object object)
官方文档中,有一段对 session_key的生命周期的描述。
session_key的生命周期有不确定性,可以使用wx.login接口刷新session_key。为了避免频繁调用wx.login接口,可以通过调用wx.checkSession(Object object)接口判断session_key是否已经过期。- 当开发者在实现自定义登录态时,可以考虑以
session_key有效期作为自身登录态有效期,也可以实现自定义的时效性策略。
wx.checkSession 的功能,可以使用此接口判断session_key是否过期。
- 调用成功说明当前
session_key未过期。 - 调用失败说明
session_key已过期。
wx.login接口仅能获取到微信登录者的有限数据,如果想要获取到登录者的更多个人信息,可以使用用户信息接口中的相关API。
wx.getUserProfile(Object object)。获取用户信息,页面产生点击事件(例如button上bindtap的回调中)后才可调用,每次请求都会弹出授权窗口,用户同意后返回userInfo。wx.getUserInfo(Object object)。和wx.getUserProfile的功能一样,在基础库 2.10 的后续版本中,其功能已经被削弱。UserInfo是用户信息封装类。
getUserProfile是从 基础库2.10.4版本开始支持的接口,该接口用来替换 wx.getUserInfo,意味着官方不建议再使用getUserInfo接口获取用户的个人信息。
下图是官方提供的 2 个接口的功能对比图。
为了避免频繁弹窗,可以在第一次获取到用户信息后保存在数据库中以备以后所用。为了获取到用户的敏感数据,在后台要通过getUserProfile接口所获取的数据进行解密操作。
wx.getUserProfile
下面通过具体代码讲解如何保存微信登录者的个人数据。先了解一下整个数据获取的流程,这里直接截取官方提供的一张流程图。
获取微信登录者的个人信息,需要经过 2 个步骤。
签名效验:
- 通过调用
wx.getUserProfile接口获取数据时,接口会同时返回rawData、signature,其中signature = sha1( rawData + session_key )。 - 开发者将
signature、rawData发送到开发者服务器进行校验。服务器利用用户对应的session_key使用相同的算法计算出签名signature2,比对signature与signature2即可校验数据的完整性。
解密加密数据:
- 对称解密使用的算法为
AES-128-CBC,数据采用PKCS#7填充。 - 对称解密的目标密文为
Base64_Decode(encryptedData)。 - 对称解密秘钥
aeskey = Base64_Decode(session_key),aeskey是16字节。 - 对称解密算法初始向量 为
Base64_Decode(iv),其中iv由数据接口返回。
具体编写实现。
第一步:在微信小程序端编码。
在index.wxml页面中添加一个按钮,并注册bindtap事件。
<view>
<button bindtap="getUserProfile">获取用户数据</button>
</view>
在index.js中添加一个名为getUserProfile的事件回调函数。为了避免不必要的弹窗,只有当后台没有获取到个人数据时,才调用wx.getUserProfile接口。
getUserProfile: function (e) {
let this_ = this
if (!this.data.isHasUserInfo) {
//如果服务器端没有保存完整的微信登录者信息
wx.getUserProfile({
desc: '需要完善您的资料!',
success: (res) => {
this_.setData({
//小程序中用来显示个人信息
userInfo: res.userInfo,
isHasUserInfo: true
})
//再次登录,因为 session_key 有生命中周期
wx.login({
success(res_) {
//保存到服务器端
let config = {
url: "wx/wxLogin",
method: "GET",
data: {
code: res_.code,
//明文数据
rawData: res.rawData,
//加密数据
encryptedData: res.encryptedData,
iv: res.iv,
//数字签名
signature: res.signature
}
}
let promise = httpRequest.wxRequest(config)
promise.then(res => {
//返回
console.log("wxLogin", res)
}).catch(res => {
console.log("fail", res)
});
}
})
}
})
}
}
服务器端代码:
在pom.xml文件中添加如下依赖包,用来解密数据。
<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响应方法。
@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;
}
}
业务代码:
小程序中传递过来的数据是经过base64编码以及加密的数据,需要使用 Base64解码字符串,再使用解密算法解密数据。先提供一个解密方法。
public String decrypt(String session_key, String iv, String encryptData) {
String decryptString = "";
//解码经过 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;
}
具体获取数据的业务实现:
@Override
public WxUserInfo getWxUserInfo(@NotNull String code, @NotNull String rawData, @NotNull String encryptedData, @NotNull String iv, @NotNull String signature) throws Exception {
//会话密钥
WxUserInfo wxUserInfo = this.getLoginCertificate(code);
String signature2 = DigestUtils.sha1Hex(rawData + wxUserInfo.getSessionKey());
if (!signature.equals(signature2)) {
throw new Exception("数字签名验证失败");
}
//数字签名验证成功,解密
String infos = this.decrypt(wxUserInfo.getSessionKey(), iv, encryptedData);
//反序列化 JSON 数据
WxUserInfo wxUserInfo_ = JSONObject.parseObject(infos, WxUserInfo.class);
wxUserInfo_.setSessionKey(wxUserInfo.getSessionKey());
wxUserInfo_.setOpenId(wxUserInfo.getOpenId());
//更新数据库
this.wxUserMapper.updateById(wxUserInfo_);
return wxUserInfo_;
}
测试,启动微信小程序和后台应用,在小程序中触发按钮事件。
在弹出的对话框中,选择允许。
查看后台数据库表中的数据。
能够获取到的微信登录者个人信息都保存到了数据库表中。至于怎么使用这些数据,可以根据自己的业务需要定制。
3.总结微信开发平台,提供有诸多接口,可以帮助开发者获取到有用的数据。本文主要介绍 wx.login和wx.getProfile接口,因篇幅所限,不能对其它接口做详细介绍 ,有兴趣者可以查阅官方文档。
官方文档只会对接口功能做些介绍 ,如要灵活运用这些接口,还需要结合实际需要演练一下,如此方能有切身体会。
本文共计4416个文字,预计阅读时间需要18分钟。
1.+ 微信小程序 + 开发平台,提供一类API,开发者可获取微信登录用户的个人数据。这类API通常称为开放接口。+ 提示:微信小程序开发平台会将微信登录用户的个人信息分发给开发者。
1. 前言微信小程序开发平台,提供有一类 API,可以让开发者获取到微信登录用户的个人数据。这类 API 统称为开放接口。
Tip:微信小程序开发平台,会把微信登录用户的个人信息分为明文数据和敏感数据。
明文数据也称为公开数据,开发者可以直接获取到,如登录者的昵称、头像……
敏感数据如电话号码、唯一标识符……等数据,只有高级认证开发者和经过登录者授权后才能解密获取到。
这一类 API较多,且 API之间功能有重叠之处,相互之间的区别较微小。有的适用于低版本,有的适用于高版本。
为了避免在使用时出现选择混乱,本文将通过具体应用案例介绍几个常用 API的使用。
开放接口是对一类 API的统称,开发者可以通过调用这类接口得到微信登录用户的授权或获取登录者的个人数据。
开放接口又分成几个子类 API :
- 登录接口: 包括
wx.pluginLogin(Object args)、wx.login(Object object)、wx.checkSession(Object object)几 个API。 - 账号信息: 包括
Object wx.getAccountInfoSync()此接口用来获取开发者的账号信息。 - 用户信息: 包括
wx.getUserProfile(Object object)、wx.getUserInfo(Object object)、UserInfo。使用频率非常高的接口,常用于小程序中获取登录者个人公开数据。 - 授权接口:
wx.authorizeForMiniProgram(Object object)、wx.authorize(Object object)
除上述列出的子类接口,还有收货地址、生物认证……等诸多子类 API,有兴趣者可以自行了解。
登录接口中有 3 个 API,对于开发者来说,使用频率较高的是 login接口,此环节将重点介绍此接口。
非本文特别关注的接口,会简略带过。
wx.pluginLogin(Object args):此接口只能在插件中可以调用,调用此接口获得插件用户的标志凭证code,插件可使用此凭证换取用于识别用户的唯一标识 OpenpId。
用户不同、宿主小程序不同或插件不同的情况下,该标识均不相同,即当且仅当同一个用户在同一个宿主小程序中使用同一个插件时,OpenpId 才会相同。
对于一般开发者,此 接口用的不是很多,具体使用细节在此处也不做过多复述。
2.1.1什么是
OpenId?当微信用户登录公众号或小程序时,微信平台为每一个微信登录者分配的一个唯一标识符号。
wx.login(Object object)
功能描述:
-
开发者使用此接口可以获取到
微信登录者的登录凭证(code)。登录凭证具有临时性,也就是每次调用时都会不一样,所以code只能使用一次。 -
开发者可以通过临时
code,再向微信接口服务器索取登录者的唯一标识符OpenId、微信开发平台账号的唯一标识UnionID(需要当前小程序已绑定到微信开放平台帐号)、以及会话密钥session_key。
那么,获取到的openId和session_key对于开发者而言,有什么实质性的意义?
-
根据
OpenId的唯一性特点,可以在微信用户第一次登录时,把OpenID保存在数据库或缓存中,在后续登录时,只需要检查用户的OpenId是否存在于数据库或缓存中,便能实现自动登录功能。 -
session_key也称会话密钥,用来解密微信登录者的敏感数据。后文将详细介绍。
如何获取OpenId?
现通过一个简单案例,实现微信小程序端与开发者服务器之间的数据交互。以此了解开发者服务器如何通过微信小程序传递过来的用户临时 code换取到登录者的更多信息。
实现之前,先通过一个简易演示图了解其过程。
简单描述整个请求过程:
- 微信用户打开微信小程序后,开发者在微信小程序中通过调用
wx.login接口获取到临时登录凭证code。 - 在微信小程序中调用
wx.request接口向开发者服务器发送127.0.0.1:8080/wx/getLoginCertificate是开发者服务器提供的对外处理微信用户信息的接口。 - 最后只是简单地输出开发者服务器端返回的数据。
127.0.0.1:8080/" //超时 let timeout = config.timeout == null ? 50000 : config.timeout; //目标地址,基地址+接口 let url = serverUrl + config.url; //数据提交方式 let method = config.method == null ? "GET" : config.method; //提交数据 let data = config.data == null ? null : config.data //头信息 let header = { // 默认值 '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, //返回的数据类型(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) } }) }) }第三步:创建开发者服务器程序(后台应用)
本文使用
spring boot快速搭建后台应用程序。在项目的pom.xml文件中除了必要的依赖包外,还需要添加以下 的依赖包。<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.73</version> </dependency> <dependency> <groupId>org.apache.api.weixin.qq.com/sns/jscode2session是微信接口服务器对外提供的接口,请求此接口时,需要提供4个请求数据。appid:小程序 appId。secret:小程序 appSecret。js_code:获取到的微信登录者的临时code。grant_type:授权类型,此处只需填写authorization_code。
public class WxUtil {
private final static String APP_ID = "微信小程序开发者申请的 appid";
private final static String APP_SECRET = "微信小程序开发者申请的 APP_SECRET";
//
private final static String WX_LOGIN_SERVER_URL = "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也是一个自定义组件,用来向指定的服务器发送http请求。
public class HttpClientUtils {
/**
* GET请求
*/
public static String getRequest(String url) throws Exception {
//HttpClient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
try {
HttpGet httpGet = new HttpGet(url);
response = httpClient.execute(httpGet);
//响应体
HttpEntity entity = response.getEntity();
if (entity != null) {
//格式化响应体
return EntityUtils.toString(entity);
}
} catch (ClientProtocolException e) {
throw e;
} catch (IOException e) {
throw e;
} finally {
response.close();
httpClient.close();
}
return null;
}
}
WxUserInfo是自定义的数据封装类。微信接口服务器返回的数据是以JSON格式组装的,这里需要格式成对象数据,便于在java中处理。本文使用MyBatisPlus操作数据库,此类也对应数据库中的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;
//会话密钥
@TableField(value = "session_key")
private String sessionKey;
//头像路径
@TableField("avatar_url")
private String avatarUrl;
//城市
private String city;
//国家
private String country;
//性别
private String gender;
//语言
private String language;
//昵称
@TableField("nick_name")
private String nickName;
//备注名或真实名
@TableField("real_name")
private String realName;
//省份
private String province;
//学生ID
@TableField("stu_id")
private Integer stuId;
}
MyBatis 数据库映射组件:
@Repository
public interface WxUserMapper extends BaseMapper<WxUserInfo> {
}
第四步:测试。
先启动后台应用程序,再启动微信小程序,可以在数据库表中查看到如下信息。
微信用户的openid和session_key已经保存到后台的数据库表中。
wx.checkSession(Object object)
官方文档中,有一段对 session_key的生命周期的描述。
session_key的生命周期有不确定性,可以使用wx.login接口刷新session_key。为了避免频繁调用wx.login接口,可以通过调用wx.checkSession(Object object)接口判断session_key是否已经过期。- 当开发者在实现自定义登录态时,可以考虑以
session_key有效期作为自身登录态有效期,也可以实现自定义的时效性策略。
wx.checkSession 的功能,可以使用此接口判断session_key是否过期。
- 调用成功说明当前
session_key未过期。 - 调用失败说明
session_key已过期。
wx.login接口仅能获取到微信登录者的有限数据,如果想要获取到登录者的更多个人信息,可以使用用户信息接口中的相关API。
wx.getUserProfile(Object object)。获取用户信息,页面产生点击事件(例如button上bindtap的回调中)后才可调用,每次请求都会弹出授权窗口,用户同意后返回userInfo。wx.getUserInfo(Object object)。和wx.getUserProfile的功能一样,在基础库 2.10 的后续版本中,其功能已经被削弱。UserInfo是用户信息封装类。
getUserProfile是从 基础库2.10.4版本开始支持的接口,该接口用来替换 wx.getUserInfo,意味着官方不建议再使用getUserInfo接口获取用户的个人信息。
下图是官方提供的 2 个接口的功能对比图。
为了避免频繁弹窗,可以在第一次获取到用户信息后保存在数据库中以备以后所用。为了获取到用户的敏感数据,在后台要通过getUserProfile接口所获取的数据进行解密操作。
wx.getUserProfile
下面通过具体代码讲解如何保存微信登录者的个人数据。先了解一下整个数据获取的流程,这里直接截取官方提供的一张流程图。
获取微信登录者的个人信息,需要经过 2 个步骤。
签名效验:
- 通过调用
wx.getUserProfile接口获取数据时,接口会同时返回rawData、signature,其中signature = sha1( rawData + session_key )。 - 开发者将
signature、rawData发送到开发者服务器进行校验。服务器利用用户对应的session_key使用相同的算法计算出签名signature2,比对signature与signature2即可校验数据的完整性。
解密加密数据:
- 对称解密使用的算法为
AES-128-CBC,数据采用PKCS#7填充。 - 对称解密的目标密文为
Base64_Decode(encryptedData)。 - 对称解密秘钥
aeskey = Base64_Decode(session_key),aeskey是16字节。 - 对称解密算法初始向量 为
Base64_Decode(iv),其中iv由数据接口返回。
具体编写实现。
第一步:在微信小程序端编码。
在index.wxml页面中添加一个按钮,并注册bindtap事件。
<view>
<button bindtap="getUserProfile">获取用户数据</button>
</view>
在index.js中添加一个名为getUserProfile的事件回调函数。为了避免不必要的弹窗,只有当后台没有获取到个人数据时,才调用wx.getUserProfile接口。
getUserProfile: function (e) {
let this_ = this
if (!this.data.isHasUserInfo) {
//如果服务器端没有保存完整的微信登录者信息
wx.getUserProfile({
desc: '需要完善您的资料!',
success: (res) => {
this_.setData({
//小程序中用来显示个人信息
userInfo: res.userInfo,
isHasUserInfo: true
})
//再次登录,因为 session_key 有生命中周期
wx.login({
success(res_) {
//保存到服务器端
let config = {
url: "wx/wxLogin",
method: "GET",
data: {
code: res_.code,
//明文数据
rawData: res.rawData,
//加密数据
encryptedData: res.encryptedData,
iv: res.iv,
//数字签名
signature: res.signature
}
}
let promise = httpRequest.wxRequest(config)
promise.then(res => {
//返回
console.log("wxLogin", res)
}).catch(res => {
console.log("fail", res)
});
}
})
}
})
}
}
服务器端代码:
在pom.xml文件中添加如下依赖包,用来解密数据。
<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响应方法。
@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;
}
}
业务代码:
小程序中传递过来的数据是经过base64编码以及加密的数据,需要使用 Base64解码字符串,再使用解密算法解密数据。先提供一个解密方法。
public String decrypt(String session_key, String iv, String encryptData) {
String decryptString = "";
//解码经过 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;
}
具体获取数据的业务实现:
@Override
public WxUserInfo getWxUserInfo(@NotNull String code, @NotNull String rawData, @NotNull String encryptedData, @NotNull String iv, @NotNull String signature) throws Exception {
//会话密钥
WxUserInfo wxUserInfo = this.getLoginCertificate(code);
String signature2 = DigestUtils.sha1Hex(rawData + wxUserInfo.getSessionKey());
if (!signature.equals(signature2)) {
throw new Exception("数字签名验证失败");
}
//数字签名验证成功,解密
String infos = this.decrypt(wxUserInfo.getSessionKey(), iv, encryptedData);
//反序列化 JSON 数据
WxUserInfo wxUserInfo_ = JSONObject.parseObject(infos, WxUserInfo.class);
wxUserInfo_.setSessionKey(wxUserInfo.getSessionKey());
wxUserInfo_.setOpenId(wxUserInfo.getOpenId());
//更新数据库
this.wxUserMapper.updateById(wxUserInfo_);
return wxUserInfo_;
}
测试,启动微信小程序和后台应用,在小程序中触发按钮事件。
在弹出的对话框中,选择允许。
查看后台数据库表中的数据。
能够获取到的微信登录者个人信息都保存到了数据库表中。至于怎么使用这些数据,可以根据自己的业务需要定制。
3.总结微信开发平台,提供有诸多接口,可以帮助开发者获取到有用的数据。本文主要介绍 wx.login和wx.getProfile接口,因篇幅所限,不能对其它接口做详细介绍 ,有兴趣者可以查阅官方文档。
官方文档只会对接口功能做些介绍 ,如要灵活运用这些接口,还需要结合实际需要演练一下,如此方能有切身体会。

