小程序登录流程图如下:
微信开发文档链接:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html
(1)前端调用 wx.login() 获取 code ,传到开发者服务器。服务端相关代码:
@GetMapping("getOpenId/{code}") public Wrapper<?> getOpenId(@PathVariable String code) { if (StringUtils.isEmpty(code)) { return WrapMapper.error(ErrorCodeEnum.ILLEGAL_ARGUMENT); } Map<String, String> map = appletService.getOpenId(code); return CollectionUtils.isEmpty(map) ? WrapMapper.error("请求失败!") : WrapMapper.ok(map); }
public final static String URL = "https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=CODE&grant_type=authorization_code"; @Value("${wx.appid}") private String appid; @Value("${wx.appsecret}") private String appsecret; @Override public Map<String, String> getOpenId(String code) { String requestUrl = URL.replace("APPID", appid).replace("SECRET", appsecret).replace("CODE", code); CloseableHttpClient client = HttpClientBuilder.create().build(); Map<String, String> result = new HashMap<>(); CloseableHttpResponse response = null; try { HttpGet httpGet = new HttpGet(requestUrl); response = client.execute(httpGet); HttpEntity responseEntity = response.getEntity(); String entry = EntityUtils.toString(responseEntity); log.info("响应状态为:" + response.getStatusLine()); if (!StringUtils.isEmpty(entry)) { log.info("响应内容为:" + entry); JSONObject json = JSONObject.parseObject(entry); String openid = String.valueOf(json.get("openid")); String sessionKey = String.valueOf(json.get("session_key")); String unionid = String.valueOf(json.get("unionid")); String errcode = String.valueOf(json.get("errcode")); String errmsg = String.valueOf(json.get("errmsg")); result.put("openid", openid); result.put("sessionKey", sessionKey); result.put("unionid", unionid); result.put("errcode", errcode); result.put("errmsg", errmsg); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (client != null) { client.close(); } if (response != null) { response.close(); } } catch (IOException e) { e.printStackTrace(); } } return result; }
(2)前端通过wx.getUserInfo获取加密过的数据encryptedData和解密参数iv,传递到后端获取手机号
@PostMapping("getPhoneNumber") public Wrapper<?> getPhoneNumber(@RequestParam("encryptedData") String encryptedData, @RequestParam("sessionKey") String sessionKey, @RequestParam("iv") String iv) { if (StringUtils.isEmpty(encryptedData) || StringUtils.isEmpty(sessionKey) || StringUtils.isEmpty(iv)) { return WrapMapper.error(ErrorCodeEnum.ILLEGAL_ARGUMENT); } String phoneNumber = appletService.getPhoneNumber(encryptedData, sessionKey, iv); return WrapMapper.ok(phoneNumber); }
@Override public String getPhoneNumber(String encryptedData, String sessionKey, String iv) { byte[] dataByte = Base64.decode(encryptedData); byte[] keyByte = Base64.decode(sessionKey); byte[] ivByte = Base64.decode(iv); try { int base = 16; if (keyByte.length % base != 0) { int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0); byte[] temp = new byte[groups * base]; Arrays.fill(temp, (byte) 0); System.arraycopy(keyByte, 0, temp, 0, keyByte.length); keyByte = temp; } Security.addProvider(new BouncyCastleProvider()); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKeySpec spec = new SecretKeySpec(keyByte, "AES"); AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES"); parameters.init(new IvParameterSpec(ivByte)); cipher.init(Cipher.DECRYPT_MODE, spec, parameters); byte[] resultByte = cipher.doFinal(dataByte); if (null != resultByte && resultByte.length > 0) { String result = new String(resultByte, StandardCharsets.UTF_8); log.info("获取手机号信息:{}", JSONObject.parseObject(result)); return (String) JSONObject.parseObject(result).get("phoneNumber"); } } catch (Exception e) { e.printStackTrace(); } return null; }
(3)将手机号和应用app做关联,获取到app中对应的用户信息。