微信小程序:java後臺獲取openId
阿新 • • 發佈:2018-08-12
+= rspec post main 接口 common https output rgs
一、功能描述
openId是某個微信賬戶對應某個小程序或者公眾號的唯一標識,但openId必須經過後臺解密才能獲取(之前實現過前臺解密,可是由於微信小程序的種種限制,前臺解密無法在小程序發布後使用)
二、實現流程
1. 獲取微信用戶的登錄信息;
2. 將encryptedData中的數據作為參數傳給java後臺
3. java後臺進行解密
三、代碼實現
1. 後臺的解密代碼
1 /** 2 * decoding encrypted data to get openid 3 * 4 * @param iv 5 * @param encryptedData6 * @param code 7 * @return 8 */ 9 @RequestMapping(value = "/decodeUserInfo", method = RequestMethod.GET) 10 private Map decodeUserInfo(String iv, String encryptedData, String code) { 11 Map map = new HashMap(); 12 // login code can not be null 13 if (code == null|| code.length() == 0) { 14 map.put("status", 0); 15 map.put("msg", "code 不能為空"); 16 return map; 17 } 18 // mini-Program‘s AppID 19 String wechatAppId = "你的小程序的AppID"; 20 21 // mini-Program‘s session-key 22 String wechatSecretKey = "你的小程序的session-key"; 23 24 String grantType = "authorization_code"; 25 26 // using login code to get sessionId and openId 27 String params = "appid=" + wechatAppId + "&secret=" + wechatSecretKey + "&js_code=" + code + "&grant_type=" + grantType; 28 29 // sending request 30 String sr = HttpRequest.sendGet("https://api.weixin.qq.com/sns/jscode2session", params); 31 32 // analysis request content 33 JSONObject json = JSONObject.fromObject(sr); 34 35 // getting session_key 36 String sessionKey = json.get("session_key").toString(); 37 38 // getting open_id 39 String openId = json.get("openid").toString(); 40 41 // decoding encrypted info with AES 42 try { 43 String result = AesCbcUtil.decrypt(encryptedData, sessionKey, iv, "UTF-8"); 44 if (null != result && result.length() > 0) { 45 map.put("status", 1); 46 map.put("msg", "解密成功"); 47 48 JSONObject userInfoJSON = JSONObject.fromObject(result); 49 Map userInfo = new HashMap(); 50 userInfo.put("openId", userInfoJSON.get("openId")); 51 userInfo.put("nickName", userInfoJSON.get("nickName")); 52 userInfo.put("gender", userInfoJSON.get("gender")); 53 userInfo.put("city", userInfoJSON.get("city")); 54 userInfo.put("province", userInfoJSON.get("province")); 55 userInfo.put("country", userInfoJSON.get("country")); 56 userInfo.put("avatarUrl", userInfoJSON.get("avatarUrl")); 57 userInfo.put("unionId", userInfoJSON.get("unionId")); 58 map.put("userInfo", userInfo); 59 return map; 60 } 61 62 63 } catch (Exception e) { 64 e.printStackTrace(); 65 } 66 map.put("status", 0); 67 map.put("msg", "解密失敗"); 68 return map; 69 }
2. 前臺代碼
1 wx.login({ 2 success: function (res) { 3 that.globalData.code = res.code;//登錄憑證 4 if (that.globalData.code) { 5 //2、調用獲取用戶信息接口 6 // 查看是否授權 7 wx.getUserInfo({ 8 success: function (res) { 9 that.globalData.encryptedData = res.encryptedData 10 that.globalData.iv = res.iv 11 console.log(‘[INFO] app.js/ ‘,{ encryptedData: res.encryptedData, iv: res.iv, code: that.globalData.code }) 12 //3.請求自己的服務器,解密用戶信息 獲取unionId等加密信息 13 wx.request({ 14 url: ‘https://www.****.cn/***/****/decodeUserInfo‘,//自己的服務接口地址 15 method: ‘get‘, 16 header: { 17 "Content-Type": "applciation/json" 18 }, 19 data: { encryptedData: res.encryptedData, iv: res.iv, code: that.globalData.code }, 20 success: function (data) { 21 22 //4.解密成功後 獲取自己服務器返回的結果 23 if (data.data.status == 1) { 24 var userInfos = data.data.userInfo; 25 that.globalData.openId = userInfos.openId; 26 console.log(‘[INFO] app.js/ userInfo:‘,userInfos) 27 } else { 28 console.log(‘[INFO] app.js/ 解密失敗‘) 29 } 30 }, 31 fail: function () { 32 console.log(‘[INFO] app.js/ 系統錯誤‘) 33 } 34 }) 35 }, 36 fail: function () { 37 console.log(‘[INFO] app.js/ 獲取用戶信息失敗‘) 38 } 39 }) 40 } else { 41 console.log(‘[INFO] app.js/ 獲取用戶登錄態失敗!‘ + r.errMsg) 42 } 43 }, 44 fail: function () { 45 console.log(‘[INFO] app.js/ 登陸失敗‘) 46 } 47 }) 48 49 // 獲取用戶信息 50 wx.getSetting({ 51 success: res => { 52 if (res.authSetting[‘scope.userInfo‘]) { 53 // 已經授權,可以直接調用 getUserInfo 獲取頭像昵稱,不會彈框 54 wx.getUserInfo({ 55 success: res => { 56 // 可以將 res 發送給後臺解碼出 unionId 57 this.globalData.userInfo = res.userInfo; 58 59 // 由於 getUserInfo 是網絡請求,可能會在 Page.onLoad 之後才返回 60 // 所以此處加入 callback 以防止這種情況 61 if (this.userInfoReadyCallback) { 62 this.userInfoReadyCallback(res) 63 } 64 } 65 }) 66 } 67 } 68 }) 69 },
2. HttpRequest工具類
1 import java.io.BufferedReader; 2 import java.io.IOException; 3 import java.io.InputStreamReader; 4 import java.io.PrintWriter; 5 import java.net.URL; 6 import java.net.URLConnection; 7 import java.util.List; 8 import java.util.Map; 9 10 public class HttpRequest { 11 12 public static void main(String[] args) { 13 //發送 GET 請求 14 String s=HttpRequest.sendGet("http://v.qq.com/x/cover/kvehb7okfxqstmc.html?vid=e01957zem6o", ""); 15 System.out.println(s); 16 17 // //發送 POST 請求 18 // String sr=HttpRequest.sendPost("http://www.toutiao.com/stream/widget/local_weather/data/?city=%E4%B8%8A%E6%B5%B7", ""); 19 // JSONObject json = JSONObject.fromObject(sr); 20 // System.out.println(json.get("data")); 21 } 22 23 /** 24 * 向指定URL發送GET方法的請求 25 * 26 * @param url 27 * 發送請求的URL 28 * @param param 29 * 請求參數,請求參數應該是 name1=value1&name2=value2 的形式。 30 * @return URL 所代表遠程資源的響應結果 31 */ 32 public static String sendGet(String url, String param) { 33 String result = ""; 34 BufferedReader in = null; 35 try { 36 String urlNameString = url + "?" + param; 37 URL realUrl = new URL(urlNameString); 38 // 打開和URL之間的連接 39 URLConnection connection = realUrl.openConnection(); 40 // 設置通用的請求屬性 41 connection.setRequestProperty("accept", "*/*"); 42 connection.setRequestProperty("connection", "Keep-Alive"); 43 connection.setRequestProperty("user-agent", 44 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); 45 // 建立實際的連接 46 connection.connect(); 47 // 獲取所有響應頭字段 48 Map<String, List<String>> map = connection.getHeaderFields(); 49 // 遍歷所有的響應頭字段 50 for (String key : map.keySet()) { 51 System.out.println(key + "--->" + map.get(key)); 52 } 53 // 定義 BufferedReader輸入流來讀取URL的響應 54 in = new BufferedReader(new InputStreamReader( 55 connection.getInputStream())); 56 String line; 57 while ((line = in.readLine()) != null) { 58 result += line; 59 } 60 } catch (Exception e) { 61 System.out.println("發送GET請求出現異常!" + e); 62 e.printStackTrace(); 63 } 64 // 使用finally塊來關閉輸入流 65 finally { 66 try { 67 if (in != null) { 68 in.close(); 69 } 70 } catch (Exception e2) { 71 e2.printStackTrace(); 72 } 73 } 74 return result; 75 } 76 77 /** 78 * 向指定 URL 發送POST方法的請求 79 * 80 * @param url 81 * 發送請求的 URL 82 * @param param 83 * 請求參數,請求參數應該是 name1=value1&name2=value2 的形式。 84 * @return 所代表遠程資源的響應結果 85 */ 86 public static String sendPost(String url, String param) { 87 PrintWriter out = null; 88 BufferedReader in = null; 89 String result = ""; 90 try { 91 URL realUrl = new URL(url); 92 // 打開和URL之間的連接 93 URLConnection conn = realUrl.openConnection(); 94 // 設置通用的請求屬性 95 conn.setRequestProperty("accept", "*/*"); 96 conn.setRequestProperty("connection", "Keep-Alive"); 97 conn.setRequestProperty("user-agent", 98 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); 99 // 發送POST請求必須設置如下兩行 100 conn.setDoOutput(true); 101 conn.setDoInput(true); 102 // 獲取URLConnection對象對應的輸出流 103 out = new PrintWriter(conn.getOutputStream()); 104 // 發送請求參數 105 out.print(param); 106 // flush輸出流的緩沖 107 out.flush(); 108 // 定義BufferedReader輸入流來讀取URL的響應 109 in = new BufferedReader( 110 new InputStreamReader(conn.getInputStream())); 111 String line; 112 while ((line = in.readLine()) != null) { 113 result += line; 114 } 115 } catch (Exception e) { 116 System.out.println("發送 POST 請求出現異常!"+e); 117 e.printStackTrace(); 118 } 119 //使用finally塊來關閉輸出流、輸入流 120 finally{ 121 try{ 122 if(out!=null){ 123 out.close(); 124 } 125 if(in!=null){ 126 in.close(); 127 } 128 } 129 catch(IOException ex){ 130 ex.printStackTrace(); 131 } 132 } 133 return result; 134 } 135 }
3. AesCbuUtil工具類
1 import org.apache.commons.codec.binary.Base64; 2 import org.bouncycastle.jce.provider.BouncyCastleProvider; 3 4 import javax.crypto.BadPaddingException; 5 import javax.crypto.Cipher; 6 import javax.crypto.IllegalBlockSizeException; 7 import javax.crypto.NoSuchPaddingException; 8 import javax.crypto.spec.IvParameterSpec; 9 import javax.crypto.spec.SecretKeySpec; 10 import java.io.UnsupportedEncodingException; 11 import java.security.*; 12 import java.security.spec.InvalidParameterSpecException; 13 14 public class AesCbcUtil { 15 16 static { 17 //BouncyCastle是一個開源的加解密解決方案,主頁在http://www.bouncycastle.org/ 18 Security.addProvider(new BouncyCastleProvider()); 19 } 20 21 /** 22 * AES解密 23 * 24 * @param data //密文,被加密的數據 25 * @param key //秘鑰 26 * @param iv //偏移量 27 * @param encodingFormat //解密後的結果需要進行的編碼 28 * @return 29 * @throws Exception 30 */ 31 public static String decrypt(String data, String key, String iv, String encodingFormat) throws Exception { 32 // initialize(); 33 34 //被加密的數據 35 byte[] dataByte = Base64.decodeBase64(data.getBytes()); 36 //加密秘鑰 37 byte[] keyByte = Base64.decodeBase64(key.getBytes()); 38 //偏移量 39 byte[] ivByte = Base64.decodeBase64(iv.getBytes()); 40 41 42 try { 43 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); 44 45 SecretKeySpec spec = new SecretKeySpec(keyByte, "AES"); 46 47 AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES"); 48 parameters.init(new IvParameterSpec(ivByte)); 49 50 cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化 51 52 byte[] resultByte = cipher.doFinal(dataByte); 53 if (null != resultByte && resultByte.length > 0) { 54 String result = new String(resultByte, encodingFormat); 55 return result; 56 } 57 return null; 58 } catch (NoSuchAlgorithmException e) { 59 e.printStackTrace(); 60 } catch (NoSuchPaddingException e) { 61 e.printStackTrace(); 62 } catch (InvalidParameterSpecException e) { 63 e.printStackTrace(); 64 } catch (InvalidKeyException e) { 65 e.printStackTrace(); 66 } catch (InvalidAlgorithmParameterException e) { 67 e.printStackTrace(); 68 } catch (IllegalBlockSizeException e) { 69 e.printStackTrace(); 70 } catch (BadPaddingException e) { 71 e.printStackTrace(); 72 } catch (UnsupportedEncodingException e) { 73 e.printStackTrace(); 74 } 75 76 return null; 77 } 78 79 }
微信小程序:java後臺獲取openId