java实现微信小程序登录的流程如图所示,首先微信小程序端调用wx.login()获取临时登录凭证code,然后将code发送给我们的后端服务,后端服务使用code、appId、appSecret调用微信服务端的接口获取openId和sessionKey。到这一步,就已经实现了在微信侧的登录了,接下来就是结合自身的业务逻辑生成登录凭证(token)返回给小程序端进行后续的接口调用了。
我们这里对登录流程做了进一步的细化,要求使用用户的手机号进行登录。
因此第一步是通过临时登录凭证(code)获取到openId和sessionKey以后,将openId返回给小程序端,并将openId和sessionKey存入redis
public String getOpenId(String code) {
//构建请求url
String url = buildWeChatOpenIdUrl(code);
//发送请求
JSONObject response = requestWeChatApi(url);
//将openid -> session key存入redis
String redisKey = RedisKey.WX_OPENID + response.getString("openid");
stringRedisTemplate.opsForValue().set(redisKey,
response.getString("session_key"), 10, TimeUnit.MINUTES);
return response.getString("openid");
}
/**
* 构建获取微信openId的url
*/
private String buildWeChatOpenIdUrl(String code) {
return UriComponentsBuilder.fromHttpUrl(wxConfig.getOpenIdApiUrl())
.queryParam("appid", wxConfig.getAppId())
.queryParam("secret", wxConfig.getAppSecret())
.queryParam("js_code", code)
.queryParam("grant_type", "authorization_code")
.toUriString();
}
第二步,小程序端通过用户授权可获取用户手机号,但此时的手机号是加密状态,因此小程序端将获取到的加密信息(encryptedData)以及openId和iv发送至后端服务,后端服务可使用sessionKey和iv对加密信息(encryptedData)进行解密从而获取用户的详细信息。
public void login(LoginDTO request) {
//根据openid获取session key
String sessionKey = stringRedisTemplate.opsForValue().get(RedisKey.WX_OPENID + request.getOpenId());
if (StringUtils.isBlank(sessionKey)) {
throw new BizException("session key失效");
}
//解密用户手机号
String userPhoneNumber = getUserPhoneNumber(sessionKey, request.getIv(), request.getEncryptedData());
if (StringUtils.isBlank(userPhoneNumber)) {
throw new BizException("账号异常");
}
//todo 业务登录流程...
}
/**
* 获取用户手机号
*/
private String getUserPhoneNumber(String sessionKey, String iv, String encryptedData) {
try {
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
SecretKeySpec keySpec = new SecretKeySpec(Base64.getDecoder().decode(sessionKey), "AES");
AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
parameters.init(new IvParameterSpec(Base64.getDecoder().decode(iv)));
cipher.init(Cipher.DECRYPT_MODE, keySpec, parameters);
byte[] result = cipher.doFinal(Base64.getDecoder().decode(encryptedData));
String phoneNumberJsonStr = new String(result, StandardCharsets.UTF_8);
JSONObject phoneNumberJson = JSONObject.parseObject(phoneNumberJsonStr);
return phoneNumberJson.getString("purePhoneNumber");
} catch (Exception e) {
log.error("微信用户手机号解密异常,sessionKey:{}, encryptedData:{}, iv:{}, 错误信息:{}", sessionKey, encryptedData, iv, e.getMessage());
return null;
}
}
获取到用户手机号以后,就可以进行我们自己业务的登录流程啦~